diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 000000000..e39a2df27 --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,120 @@ +module.exports = { + env: { + browser: true, + es6: true, + commonjs: true + }, + globals: { + Atomics: 'readonly', + SharedArrayBuffer: 'readonly' + }, + parser: '@typescript-eslint/parser', + parserOptions: { + project: './tsconfig.json' + // sourceType: 'module', + // requireConfigFile: false, + // ecmaFeatures: { + // jsx: true + // }, + // ecmaVersion: 8, + // sourceType: 'module', + // settings: { + // 'import/parsers': { + // '@typescript-eslint/parser': ['.ts', '.tsx','.js','.jsx'] + // }, + // 'import/resolver': { + // typescript: {}, + // node: { + // extensions: ['.js', '.jsx', '.ts', '.tsx'] + // } + // } + // } + }, + extends: [ + 'eslint:recommended', + 'plugin:prettier/recommended', + 'plugin:react/recommended', + 'plugin:react-hooks/recommended', + 'plugin:import/errors', + 'plugin:import/recommended', + 'plugin:react/jsx-runtime', + 'plugin:jsx-a11y/recommended', + 'airbnb', + 'prettier', + 'plugin:json/recommended', + 'plugin:storybook/recommended', + 'plugin:cypress/recommended', + 'prettier' + ], + plugins: ['react', 'react-hooks', 'jsx-a11y', 'import', 'promise', 'cypress', 'prettier'], + ignorePatterns: ['vite.config.js', 'commitlint.config.js'], + rules: { + indent: 'off', + allowImplicit: 0, + semi: ['error', 'never'], + camelcase: 'error', + 'react/require-default-props': [0, { functions: 'ignore' }], + 'template-curly-spacing': 'off', + 'react/jsx-filename-extension': [1, { extensions: ['.js', '.jsx', '.ts', '.tsx'] }], + 'react/destructuring-assignment': 0, + 'arrow-parens': 0, + 'react/prop-types': 0, + 'max-len': ['error', { code: 350 }], + 'linebreak-style': ['error', 'unix'], + 'react-hooks/exhaustive-deps': 'warn', + 'react/jsx-uses-react': 'off', + 'react/react-in-jsx-scope': 'off', + 'object-curly-newline': [ + 'error', + { + ImportDeclaration: { consistent: true }, + ExportDeclaration: { consistent: true }, + ObjectPattern: { consistent: true }, + ObjectExpression: { consistent: true } + } + ], + 'array-callback-return': 'off', + 'consistent-return': 'off', + 'newline-per-chained-call': ['error', { ignoreChainWithDepth: 4 }], + 'import/no-extraneous-dependencies': [ + 'error', + { devDependencies: ['**/*.test.tsx', '**/*.test.ts'] } + ], + 'import/no-duplicates': 'error', + 'import/no-self-import': 'error', + 'import/no-relative-packages': 'error', + 'import/no-relative-parent-imports': 'error', + 'import/consistent-type-specifier-style': ['error', 'prefer-inline'], + '@typescript-eslint/consistent-type-imports': 'error', + 'import/no-empty-named-blocks': 'error', + 'import/no-extraneous-dependencies': 'error', + 'import/no-import-module-exports': 'error', + 'import/newline-after-import': 'error', + 'import/no-useless-path-segments': ['error', { noUselessIndex: true }], + '@typescript-eslint/no-non-null-assertion': 'error', + '@typescript-eslint/no-unused-vars': 'error', + '@typescript-eslint/indent': 'off', + '@typescript-eslint/semi': 'off', + 'prettier/prettier': [ + 'warn', + { + // "importOrder": [ + // "^(^react$|@react|react)", + // "", + // "^@/(.*)$", + // "^[./]" + // ], + // "importOrderSeparation": true, + // "importOrderSortSpecifiers": true, + // ...require('./.prettierrc'), + } + ], + 'react/no-unknown-property': ['error', { ignore: ['css'] }], + 'no-param-reassign': ['error', { props: true, ignorePropertyModificationsForRegex: ['(d|D)raft'] }] + // 'react/jsx-first-prop-new-line': [2, 'multiline'], + // 'react/jsx-max-props-per-line': [1, { maximum: 1, when: 'multiline' }], + // // 'react/jsx-indent-props': [0, 0], + // 'react/jsx-closing-bracket-location': [2, 'tag-aligned'], + // 'implicit-arrow-linebreak': [0, 'beside'], + } +} diff --git a/.github/build b/.github/build index 16616ded4..9205a6300 100755 --- a/.github/build +++ b/.github/build @@ -13,15 +13,13 @@ fi composer i -o --no-dev --ignore-platform-reqs 2>&1 >/dev/null -cd "$PLUGIN_DIR/frontend-dev" && pnpm pda - -cd "$PLUGIN_DIR" +cd "$PLUGIN_DIR" && pnpm pda cp -r "$PLUGIN_DIR/assets" "$BUILD_DIR/assets" cp -r "$PLUGIN_DIR/bitwpfi.php" "$BUILD_DIR/bitwpfi.php" cp -r "$PLUGIN_DIR/build-hash.txt" "$BUILD_DIR/build-hash.txt" cp -r "$PLUGIN_DIR/composer.json" "$BUILD_DIR/composer.json" -cp -r "$PLUGIN_DIR/includes" "$BUILD_DIR/includes" +cp -r "$PLUGIN_DIR/backend" "$BUILD_DIR/backend" cp -r "$PLUGIN_DIR/languages" "$BUILD_DIR/languages" cp -r "$PLUGIN_DIR/readme.txt" "$BUILD_DIR/readme.txt" cp -r "$PLUGIN_DIR/vendor" "$BUILD_DIR/vendor" diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index c7b5ff44e..6e82531fd 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -27,8 +27,8 @@ jobs: uses: actions/setup-node@v4 with: node-version: 22 - cache: "pnpm" - cache-dependency-path: "frontend-dev/pnpm-lock.yaml" + cache: 'pnpm' + cache-dependency-path: 'pnpm-lock.yaml' - name: Get pnpm store directory id: pnpm-cache @@ -46,7 +46,6 @@ jobs: - name: Install frontend dependencies run: | - cd frontend-dev pnpm install - name: Check for SVN @@ -64,7 +63,7 @@ jobs: id: setup-php uses: shivammathur/setup-php@v2 with: - php-version: "8.x" + php-version: '8.x' tools: composer:v2, wp-cli - name: Determine if this is a release or push @@ -96,7 +95,7 @@ jobs: env: SVN_USERNAME: ${{ secrets.SVN_USERNAME }} SVN_PASSWORD: ${{ secrets.SVN_PASSWORD }} - BUILD_DIR: "${{ github.workspace }}/build/${{ env.PLUGIN_SLUG }}" + BUILD_DIR: '${{ github.workspace }}/build/${{ env.PLUGIN_SLUG }}' SLUG: ${{ env.PLUGIN_SLUG }} - name: Upload release asset if: steps.build-plugin.outputs.free_exists == 'true' diff --git a/.github/workflows/plugin-check.yml b/.github/workflows/plugin-check.yml new file mode 100644 index 000000000..f88d2c71f --- /dev/null +++ b/.github/workflows/plugin-check.yml @@ -0,0 +1,95 @@ +name: WordPress Plugin Check + +on: + push: + branches: + - main + pull_request: + branches: + - main + +env: + PLUGIN_SLUG: bit-integrations + +jobs: + plugin-check: + name: Plugin Check + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 1 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: "8.1" + coverage: none + tools: composer:v2, wp-cli + + - name: Setup Node + uses: actions/setup-node@v4 + with: + node-version: 22 + + - name: Install pnpm + uses: pnpm/action-setup@v4 + with: + run_install: false + + - name: Get pnpm store directory + id: pnpm-cache + shell: bash + run: | + echo "dir=$(pnpm store path --silent)" >> $GITHUB_OUTPUT + + - name: Get composer cache directory + id: composer-cache + run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT + + - name: Setup package cache + uses: actions/cache@v4 + with: + path: | + ${{ steps.pnpm-cache.outputs.dir }} + ${{ steps.composer-cache.outputs.dir }} + key: ${{ runner.os }}-plugin-check-${{ hashFiles('**/pnpm-lock.yaml', '**/composer.lock') }} + restore-keys: | + ${{ runner.os }}-plugin-check- + + - name: Install dependencies + run: | + composer install --prefer-dist --no-progress --no-dev --optimize-autoloader + pnpm install --frozen-lockfile + + - name: Build plugin + run: | + pnpm production + bash .github/copy-assets + + - name: WordPress Plugin Check + uses: wordpress/plugin-check-action@v1 + with: + build-dir: "./build/${{ env.PLUGIN_SLUG }}" + exclude-directories: | + vendor + node_modules + categories: | + general + security + performance + accessibility + plugin_repo + checks: | + i18n_usage + late_escaping + plugin_header + plugin_readme + file_type + performant_wp_query_params + plugin_updater + enqueued_scripts_size + plugin_review_phpcs + trademarks diff --git a/.gitignore b/.gitignore index 77eeb7e7b..c1993665e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,17 +1,10 @@ .idea locale.pot assets/ -languages/ vendor/ node_modules -composer.lock -package-lock.json -pnpm-lock.yaml -yarn.lock -yarn-error.log - .php-cs-fixer.cache -build-hash.txt -build/ \ No newline at end of file +build/ +.port \ No newline at end of file diff --git a/.php-cs-fixer.php b/.php-cs-fixer.php index c25aafb6f..8108f409b 100644 --- a/.php-cs-fixer.php +++ b/.php-cs-fixer.php @@ -5,7 +5,7 @@ __DIR__ . '/vendor', ]) ->in([ - __DIR__ . '/includes', + __DIR__ . '/backend', __DIR__ . '/views', ]) ->ignoreVCSIgnored(true); @@ -80,7 +80,7 @@ 'multiline_whitespace_before_semicolons' => false, 'no_whitespace_before_comma_in_array' => true, 'native_function_casing' => true, - 'native_function_invocation' => ['include' => ['@compiler_optimized'], 'scope' => 'namespaced', 'strict' => false], + 'native_function_invocation' => ['include' => ['@compiler_optimized'], 'scope' => 'namespaced', 'strict' => false, 'exclude' => ['defined']], 'new_with_braces' => true, 'no_alias_language_construct_call' => true, 'no_alternative_syntax' => true, diff --git a/frontend-dev/.prettierrc b/.prettierrc similarity index 100% rename from frontend-dev/.prettierrc rename to .prettierrc diff --git a/.vscode/settings.json b/.vscode/settings.json index 5bf55b769..fd7d3e1cc 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -6,7 +6,7 @@ "search.useGlobalIgnoreFiles": false, "search.useParentIgnoreFiles": false, "prettier.enable": true, - "prettier.prettierPath": "frontend-dev/node_modules/prettier/index.cjs", + "prettier.prettierPath": "node_modules/prettier/index.cjs", "editor.defaultFormatter": "esbenp.prettier-vscode", "editor.formatOnSave": true, "editor.codeActionsOnSave": { diff --git a/frontend-dev/LICENSE b/LICENSE similarity index 100% rename from frontend-dev/LICENSE rename to LICENSE diff --git a/includes/Actions/ACPT/ACPTController.php b/backend/Actions/ACPT/ACPTController.php similarity index 94% rename from includes/Actions/ACPT/ACPTController.php rename to backend/Actions/ACPT/ACPTController.php index 18adbcb5e..af0fc8825 100644 --- a/includes/Actions/ACPT/ACPTController.php +++ b/backend/Actions/ACPT/ACPTController.php @@ -4,9 +4,9 @@ * ACPT Integration */ -namespace BitCode\FI\Actions\ACPT; +namespace BitApps\Integrations\Actions\ACPT; -use BitCode\FI\Core\Util\HttpHelper; +use BitApps\Integrations\Core\Util\HttpHelper; use WP_Error; /** @@ -46,6 +46,7 @@ public function execute($integrationData, $fieldValues) $module = $integrationDetails->module; if (empty($fieldMap) || empty($module) || empty($apiKey) || empty($baseUrl)) { + // translators: %s: Placeholder value return new WP_Error('REQ_FIELD_EMPTY', wp_sprintf(__('module, fields are required for %s api', 'bit-integrations'), 'ACPT')); } diff --git a/includes/Actions/ACPT/ACPTHelper.php b/backend/Actions/ACPT/ACPTHelper.php similarity index 94% rename from includes/Actions/ACPT/ACPTHelper.php rename to backend/Actions/ACPT/ACPTHelper.php index f686841a3..10fa2fe7a 100644 --- a/includes/Actions/ACPT/ACPTHelper.php +++ b/backend/Actions/ACPT/ACPTHelper.php @@ -1,9 +1,9 @@ false, + // translators: %s: Placeholder value 'message' => \sprintf(__('Required field %s is empty', 'bit-integrations'), $label), 'code' => 422, ]; @@ -129,6 +130,7 @@ public static function prepareTaxonomyData($finalData, $fieldValues, $integratio public static function validateResponse($response) { return !$response + // translators: %s: Plugin name ? ['error' => wp_sprintf(__('%s plugin is not installed or activate', 'bit-integrations'), 'Bit Integration Pro')] : $response; } @@ -144,6 +146,7 @@ private static function validateFields($requiredFields, $finalData) if (empty($finalData[$key])) { return [ 'success' => false, + // translators: %s: Placeholder value 'message' => \sprintf(__('Required field %s is empty', 'bit-integrations'), $label), 'code' => 422, ]; diff --git a/backend/Actions/ACPT/RecordApiHelper.php b/backend/Actions/ACPT/RecordApiHelper.php new file mode 100644 index 000000000..d699f8558 --- /dev/null +++ b/backend/Actions/ACPT/RecordApiHelper.php @@ -0,0 +1,439 @@ +integrationDetails = $integrationDetails; + $this->integrationId = $integId; + $this->apikey = $apiKey; + + $this->apiUrl = "{$baseUrl}/wp-json/acpt/v1"; + + $this->defaultHeader = [ + 'acpt-api-key' => $apiKey, + 'Content-Type' => 'application/json', + 'accept' => 'application/json', + ]; + } + + public function createCPT($finalData, $fieldValues) + { + if (empty($this->integrationDetails->icon)) { + return [ + 'success' => false, + 'message' => __('Required field Icon is empty', 'bit-integrations'), + 'code' => 422, + ]; + } + + if ($error = ACPTHelper::cptValidateRequired($finalData)) { + return $error; + } + + $finalData['post_name'] = ACPTHelper::convertToSlug($finalData['post_name']); + $finalData['icon'] = $this->integrationDetails->icon; + + $apiEndpoint = $this->apiUrl . '/cpt'; + $payload = ACPTHelper::prepareCPTData($finalData, $fieldValues, $this->integrationDetails); + + return HttpHelper::post($apiEndpoint, $payload, $this->defaultHeader); + } + + public function updateCPT($finalData, $fieldValues) + { + if ($error = ACPTHelper::cptValidateRequired($finalData)) { + return $error; + } + + $slug = ACPTHelper::convertToSlug($finalData['post_name']); + + $finalData['icon'] = $this->integrationDetails->icon; + + $apiEndpoint = $this->apiUrl . '/cpt/' . $slug; + $finalData = ACPTHelper::prepareCPTData($finalData, $fieldValues, $this->integrationDetails); + + $response = Hooks::apply(Config::withPrefix('acpt_update_cpt'), false, $apiEndpoint, $this->apikey, $finalData); + + /** + * @deprecated 2.7.8 Use `bit_integrations_acpt_update_cpt` filter instead. + * @since 2.7.8 + */ + $response = Hooks::apply('btcbi_acpt_update_cpt', $response, $apiEndpoint, $this->apikey, $finalData); + + return ACPTHelper::validateResponse($response); + } + + public function deleteCPT($finalData) + { + if (empty($finalData['slug'])) { + return [ + 'success' => false, + 'message' => __('Required field slug is empty', 'bit-integrations'), + 'code' => 422, + ]; + } + + $apiEndpoint = $this->apiUrl . '/cpt/' . $finalData['slug']; + + $response = Hooks::apply(Config::withPrefix('acpt_delete_cpt'), false, $apiEndpoint, $this->apikey); + + /** + * @deprecated 2.7.8 Use `bit_integrations_acpt_delete_cpt` filter instead. + * @since 2.7.8 + */ + $response = Hooks::apply('btcbi_acpt_delete_cpt', $response, $apiEndpoint, $this->apikey); + + return ACPTHelper::validateResponse($response); + } + + public function createOrUpdateTaxonomy($finalData, $fieldValues, $isUpdate = false) + { + if ($error = ACPTHelper::taxonomyValidateRequired($finalData)) { + return $error; + } + + $slug = ACPTHelper::convertToSlug($finalData['slug']); + $finalData['slug'] = $slug; + + $finalData = ACPTHelper::prepareTaxonomyData($finalData, $fieldValues, $this->integrationDetails); + + $path = $isUpdate ? '/taxonomy/' . $slug : '/taxonomy'; + $apiEndpoint = $this->apiUrl . $path; + + if ($isUpdate) { + $response = Hooks::apply(Config::withPrefix('acpt_update_taxonomy'), false, $apiEndpoint, $this->apikey, $finalData); + + /** + * @deprecated 2.7.8 Use `bit_integrations_acpt_update_taxonomy` filter instead. + * @since 2.7.8 + */ + $response = Hooks::apply('btcbi_acpt_update_taxonomy', $response, $apiEndpoint, $this->apikey, $finalData); + } else { + $response = Hooks::apply(Config::withPrefix('acpt_create_taxonomy'), false, $apiEndpoint, $this->apikey, $finalData); + + /** + * @deprecated 2.7.8 Use `bit_integrations_acpt_create_taxonomy` filter instead. + * @since 2.7.8 + */ + $response = Hooks::apply('btcbi_acpt_create_taxonomy', $response, $apiEndpoint, $this->apikey, $finalData); + } + + return ACPTHelper::validateResponse($response); + } + + public function deleteTaxonomy($finalData) + { + if (empty($finalData['slug'])) { + return [ + 'success' => false, + 'message' => __('Required field slug is empty', 'bit-integrations'), + 'code' => 422, + ]; + } + + $slug = ACPTHelper::convertToSlug($finalData['slug']); + + $apiEndpoint = $this->apiUrl . '/taxonomy/' . $slug; + + $response = Hooks::apply(Config::withPrefix('acpt_delete_taxonomy'), false, $apiEndpoint, $this->apikey); + + /** + * @deprecated 2.7.8 Use `bit_integrations_acpt_delete_taxonomy` filter instead. + * @since 2.7.8 + */ + $response = Hooks::apply('btcbi_acpt_delete_taxonomy', $response, $apiEndpoint, $this->apikey); + + return ACPTHelper::validateResponse($response); + } + + public function associateTaxonomyToCPT($finalData) + { + if (empty($finalData['taxonomy_slug'])) { + return [ + 'success' => false, + 'message' => __('Required field taxonomy slug is empty', 'bit-integrations'), + 'code' => 422, + ]; + } + if (empty($finalData['cpt_slug'])) { + return [ + 'success' => false, + 'message' => __('Required field cpt slug is empty', 'bit-integrations'), + 'code' => 422, + ]; + } + + $finalData['taxonomy_slug'] = ACPTHelper::convertToSlug($finalData['taxonomy_slug']); + $finalData['cpt_slug'] = ACPTHelper::convertToSlug($finalData['cpt_slug']); + + $apiEndpoint = $this->apiUrl . '/taxonomy/assoc/' . $finalData['taxonomy_slug'] . '/' . $finalData['cpt_slug']; + + $response = Hooks::apply(Config::withPrefix('acpt_associate_taxonomy_to_cpt'), false, $apiEndpoint, $this->apikey); + + /** + * @deprecated 2.7.8 Use `bit_integrations_acpt_associate_taxonomy_to_cpt` filter instead. + * @since 2.7.8 + */ + $response = Hooks::apply('btcbi_acpt_associate_taxonomy_to_cpt', $response, $apiEndpoint, $this->apikey); + + return ACPTHelper::validateResponse($response); + } + + public function createOrUpdateOptionPage($finalData, $isUpdate = false) + { + if (empty($this->integrationDetails->icon)) { + return [ + 'success' => false, + 'message' => __('Required field Icon is empty', 'bit-integrations'), + 'code' => 422, + ]; + } + + if (empty($this->integrationDetails->capability)) { + return [ + 'success' => false, + 'message' => __('Required field Capability is empty', 'bit-integrations'), + 'code' => 422, + ]; + } + + if ($error = ACPTHelper::optionPageValidateRequired($finalData)) { + return $error; + } + + $finalData['icon'] = $this->integrationDetails->icon; + $finalData['capability'] = $this->integrationDetails->capability; + + $finalData['position'] = (integer) $finalData['position']; + $finalData['menuSlug'] = ACPTHelper::convertToSlug($finalData['menuSlug']); + + $path = $isUpdate ? '/option-page/' . $finalData['menuSlug'] : '/option-page'; + $apiEndpoint = $this->apiUrl . $path; + + if ($isUpdate) { + $response = Hooks::apply(Config::withPrefix('acpt_update_option_page'), false, $apiEndpoint, $this->apikey, wp_json_encode($finalData)); + + /** + * @deprecated 2.7.8 Use `bit_integrations_acpt_update_option_page` filter instead. + * @since 2.7.8 + */ + $response = Hooks::apply('btcbi_acpt_update_option_page', $response, $apiEndpoint, $this->apikey, wp_json_encode($finalData)); + } else { + $response = Hooks::apply(Config::withPrefix('acpt_create_option_page'), false, $apiEndpoint, $this->apikey, wp_json_encode($finalData)); + + /** + * @deprecated 2.7.8 Use `bit_integrations_acpt_create_option_page` filter instead. + * @since 2.7.8 + */ + $response = Hooks::apply('btcbi_acpt_create_option_page', $response, $apiEndpoint, $this->apikey, wp_json_encode($finalData)); + } + + return ACPTHelper::validateResponse($response); + } + + public function deleteOptionPage($finalData) + { + if (empty($finalData['slug'])) { + return [ + 'success' => false, + 'message' => __('Required field menu slug is empty', 'bit-integrations'), + 'code' => 422, + ]; + } + + $finalData['slug'] = ACPTHelper::convertToSlug($finalData['slug']); + + $apiEndpoint = $this->apiUrl . '/option-page/' . $finalData['slug']; + + $response = Hooks::apply(Config::withPrefix('acpt_delete_option_page'), false, $apiEndpoint, $this->apikey); + + /** + * @deprecated 2.7.8 Use `bit_integrations_acpt_delete_option_page` filter instead. + * @since 2.7.8 + */ + $response = Hooks::apply('btcbi_acpt_delete_option_page', $response, $apiEndpoint, $this->apikey); + + return ACPTHelper::validateResponse($response); + } + + public function deleteMetaGroup($finalData) + { + if (empty($finalData['id'])) { + return [ + 'success' => false, + 'message' => __('Required field meta group id is empty', 'bit-integrations'), + 'code' => 422, + ]; + } + + $apiEndpoint = $this->apiUrl . '/meta/' . $finalData['id']; + + $response = Hooks::apply(Config::withPrefix('acpt_delete_meta_group'), false, $apiEndpoint, $this->apikey); + + /** + * @deprecated 2.7.8 Use `bit_integrations_acpt_delete_meta_group` filter instead. + * @since 2.7.8 + */ + $response = Hooks::apply('btcbi_acpt_delete_meta_group', $response, $apiEndpoint, $this->apikey); + + return ACPTHelper::validateResponse($response); + } + + public function deleteDynamicBlock($finalData) + { + if (empty($finalData['id'])) { + return [ + 'success' => false, + 'message' => __('Required field dynamic block id is empty', 'bit-integrations'), + 'code' => 422, + ]; + } + + $apiEndpoint = $this->apiUrl . '/block/' . $finalData['id']; + + HttpHelper::get($apiEndpoint, [], $this->defaultHeader); + if (HttpHelper::$responseCode != 200) { + return [ + 'success' => false, + 'message' => __('Dynamic Block Not Found!', 'bit-integrations'), + 'code' => HttpHelper::$responseCode, + ]; + } + + $response = Hooks::apply(Config::withPrefix('acpt_delete_dynamic_block'), false, $apiEndpoint, $this->apikey); + + /** + * @deprecated 2.7.8 Use `bit_integrations_acpt_delete_dynamic_block` filter instead. + * @since 2.7.8 + */ + $response = Hooks::apply('btcbi_acpt_delete_dynamic_block', $response, $apiEndpoint, $this->apikey); + + return ACPTHelper::validateResponse($response); + } + + public function execute($fieldValues, $fieldMap, $module) + { + $type = ''; + $typeName = ''; + + $finalData = ACPTHelper::generateReqDataFromFieldMap($fieldValues, $fieldMap); + + switch ($module) { + case 'create_cpt': + $type = 'CPT'; + $typeName = 'Create CPT'; + + $apiResponse = $this->createCPT($finalData, $fieldValues); + + break; + case 'update_cpt': + $type = 'CPT'; + $typeName = 'Update CPT'; + + $apiResponse = $this->updateCPT($finalData, $fieldValues); + + break; + case 'delete_cpt': + $type = 'CPT'; + $typeName = 'Delete CPT'; + + $apiResponse = $this->deleteCPT($finalData); + + break; + case 'create_taxonomy': + $type = 'Taxonomy'; + $typeName = 'Create Taxonomy'; + + $apiResponse = $this->createOrUpdateTaxonomy($finalData, $fieldValues); + + break; + case 'update_taxonomy': + $type = 'Taxonomy'; + $typeName = 'Update Taxonomy'; + + $apiResponse = $this->createOrUpdateTaxonomy($finalData, $fieldValues, true); + + break; + case 'delete_taxonomy': + $type = 'Taxonomy'; + $typeName = 'Delete Taxonomy'; + + $apiResponse = $this->deleteTaxonomy($finalData); + + break; + case 'associate_taxonomy_to_cpt': + $type = 'Associate'; + $typeName = 'Associate a Registered Taxonomy to a CPT'; + + $apiResponse = $this->associateTaxonomyToCPT($finalData); + + break; + case 'create_option_page': + $type = 'Option Page'; + $typeName = 'Create Option Page'; + + $apiResponse = $this->createOrUpdateOptionPage($finalData); + + break; + case 'update_option_page': + $type = 'Option Page'; + $typeName = 'Update Option Page'; + + $apiResponse = $this->createOrUpdateOptionPage($finalData, true); + + break; + case 'delete_option_page': + $type = 'Option Page'; + $typeName = 'Delete Option Page'; + + $apiResponse = $this->deleteOptionPage($finalData); + + break; + case 'delete_meta_group': + $type = 'Meta Field Group'; + $typeName = 'Delete Meta Field Group'; + + $apiResponse = $this->deleteMetaGroup($finalData); + + break; + case 'delete_dynamic_block': + $type = 'Dynamic Block'; + $typeName = 'Delete Dynamic Block'; + + $apiResponse = $this->deleteDynamicBlock($finalData); + + break; + } + + $type = (!empty($apiResponse->id) || \in_array(HttpHelper::$responseCode, [201, 200])) ? 'success' : 'error'; + + LogHandler::save($this->integrationId, wp_json_encode(['type' => $type, 'type_name' => $typeName]), $type, wp_json_encode($apiResponse)); + + return $apiResponse; + } +} diff --git a/backend/Actions/ACPT/Routes.php b/backend/Actions/ACPT/Routes.php new file mode 100644 index 000000000..981f2b9fb --- /dev/null +++ b/backend/Actions/ACPT/Routes.php @@ -0,0 +1,10 @@ +type; if (!class_exists('Academy')) { + // translators: %s: Plugin name wp_send_json_error(wp_sprintf(__('%s is not installed or activated', 'bit-integrations'), 'Academy Lms')); } @@ -121,6 +128,7 @@ public static function completeLesson($selectedCourse, $selectedLesson) $course_id = $selectedCourse[0]; $topic_type = 'lesson'; + // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- Using Academy LMS's own hook for compatibility do_action('academy/frontend/before_mark_topic_complete', $topic_type, $course_id, $topic_id, $user_id); $option_name = 'academy_course_' . $course_id . '_completed_topics'; @@ -133,6 +141,7 @@ public static function completeLesson($selectedCourse, $selectedLesson) } $saved_topics_lists = wp_json_encode($saved_topics_lists); update_user_meta($user_id, $option_name, $saved_topics_lists); + // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- Using Academy LMS's own hook for compatibility do_action('academy/frontend/after_mark_topic_complete', $topic_type, $course_id, $topic_id, $user_id); return 'Lesson Completed'; @@ -150,6 +159,7 @@ public static function completeCourse($selectedCourse) // hash is unique. do { $hash = substr(md5(wp_generate_password(32) . $date . $course_id . $user_id), 0, 16); + // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Direct query needed for hash validation $hasHash = (int) $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(comment_ID) from {$wpdb->comments} @@ -170,6 +180,7 @@ public static function completeCourse($selectedCourse) 'comment_type' => 'course_completed', 'user_id' => $user_id, ]; + // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Direct insert required for course completion $is_complete = $wpdb->insert($wpdb->comments, $data); do_action('academy/admin/course_complete_after', $course_id, $user_id); @@ -186,17 +197,25 @@ public static function resetCourse($selectedCourse) $course_id = $selectedCourse[0]; $complete_topics = "academy_course_{$course_id}_completed_topics"; + // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Direct queries needed for course reset $wpdb->query($wpdb->prepare("DELETE from {$wpdb->postmeta} WHERE post_id = %d AND meta_key = %s", $course_id, 'academy_course_curriculum')); + // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching $wpdb->query($wpdb->prepare("DELETE from {$wpdb->usermeta} WHERE user_id = %d AND meta_key = %s", $user_id, $complete_topics)); + // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching $wpdb->query($wpdb->prepare("DELETE from {$wpdb->posts} WHERE post_author = %d AND post_parent = %d AND post_type = %s ", $user_id, $course_id, 'academy_enrolled')); - $QuizIds = $wpdb->get_col($wpdb->prepare("select quiz_id from {$wpdb->prefix}academy_quiz_attempts where user_id = '14' AND course_id = %d ", $course_id)); + // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Direct query needed for AcademyLMS reset lookup. + $QuizIds = $wpdb->get_col($wpdb->prepare("SELECT quiz_id FROM {$wpdb->prefix}academy_quiz_attempts WHERE user_id = %d AND course_id = %d", $user_id, $course_id)); if (!empty($QuizIds)) { - $QuizIds = "'" . implode("','", $QuizIds) . "'"; + $placeholders = implode(',', array_fill(0, \count($QuizIds), '%d')); + // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching $wpdb->query($wpdb->prepare("DELETE from {$wpdb->prefix}academy_quiz_attempts WHERE user_id = %d AND course_id = %d", $user_id, $course_id)); - $wpdb->query($wpdb->prepare("DELETE from {$wpdb->prefix}academy_quiz_attempt_answers WHERE user_id = %d AND quiz_id in (%s) ", $user_id, $QuizIds)); + $query_format = \sprintf("DELETE from {$wpdb->prefix}academy_quiz_attempt_answers WHERE user_id = %%d AND quiz_id in ({$placeholders})"); + // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- Dynamic IN clause with sprintf for placeholders + $wpdb->query($wpdb->prepare($query_format, $user_id, ...$QuizIds)); } + // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching $wpdb->query($wpdb->prepare("DELETE from {$wpdb->comments} WHERE comment_agent = 'academy' AND comment_type = 'course_completed' AND comment_post_ID = %d AND user_id = %d", $course_id, $user_id)); return 'Course progress reseted'; diff --git a/backend/Actions/AcademyLms/Routes.php b/backend/Actions/AcademyLms/Routes.php new file mode 100644 index 000000000..a655d3623 --- /dev/null +++ b/backend/Actions/AcademyLms/Routes.php @@ -0,0 +1,12 @@ +get_params(); unset($params['rest_route'], $params['state']); - if (wp_redirect($state . '&' . http_build_query($params), 302)) { - exit; - } + wp_safe_redirect($state . '&' . http_build_query($params), 302); + exit; } public static function getHostWithPort($url) diff --git a/includes/Actions/ActiveCampaign/ActiveCampaignController.php b/backend/Actions/ActiveCampaign/ActiveCampaignController.php similarity index 98% rename from includes/Actions/ActiveCampaign/ActiveCampaignController.php rename to backend/Actions/ActiveCampaign/ActiveCampaignController.php index af970ba4b..d3be0cbd6 100644 --- a/includes/Actions/ActiveCampaign/ActiveCampaignController.php +++ b/backend/Actions/ActiveCampaign/ActiveCampaignController.php @@ -4,9 +4,9 @@ * Active Campaign Integration */ -namespace BitCode\FI\Actions\ActiveCampaign; +namespace BitApps\Integrations\Actions\ActiveCampaign; -use BitCode\FI\Core\Util\HttpHelper; +use BitApps\Integrations\Core\Util\HttpHelper; use WP_Error; /** @@ -245,6 +245,7 @@ public function execute($integrationData, $fieldValues) || empty($api_url) || empty($fieldMap) ) { + // translators: %s: Placeholder value return new WP_Error('REQ_FIELD_EMPTY', wp_sprintf(__('module, fields are required for %s api', 'bit-integrations'), 'Active Campaign')); } $recordApiHelper = new RecordApiHelper($api_key, $api_url, $this->_integrationID); diff --git a/backend/Actions/ActiveCampaign/RecordApiHelper.php b/backend/Actions/ActiveCampaign/RecordApiHelper.php new file mode 100644 index 000000000..ff3ab33aa --- /dev/null +++ b/backend/Actions/ActiveCampaign/RecordApiHelper.php @@ -0,0 +1,193 @@ +_defaultHeader['Api-Token'] = $api_key; + $this->_apiEndpoint = $api_url . '/api/3'; + $this->_integrationID = $integId; + } + + // for insert data + public function storeOrModifyRecord($method, $data) + { + $insertRecordEndpoint = "{$this->_apiEndpoint}/{$method}"; + + return HttpHelper::post($insertRecordEndpoint, $data, $this->_defaultHeader); + } + + public function updateRecord($id, $data, $existContact) + { + $contactData = $data['contact']; + foreach ($contactData as $key => $value) { + if ($value === '') { + $contactData->{$key} = $existContact->contacts[0]->{$key}; + } + } + + $updateRecordEndpoint = "{$this->_apiEndpoint}/contacts/{$id}"; + + return HttpHelper::request($updateRecordEndpoint, 'PUT', wp_json_encode($data), $this->_defaultHeader); + } + + public function execute($integrationDetails, $fieldValues, $fieldMap, $actions, $listId, $tags) + { + $fieldData = []; + $customFields = []; + + foreach ($fieldMap as $fieldKey => $fieldPair) { + if (!empty($fieldPair->activeCampaignField)) { + if ($fieldPair->formField === 'custom' && isset($fieldPair->customValue) && !is_numeric($fieldPair->activeCampaignField)) { + $fieldData[$fieldPair->activeCampaignField] = $fieldPair->customValue; + } elseif (is_numeric($fieldPair->activeCampaignField) && $fieldPair->formField === 'custom' && isset($fieldPair->customValue)) { + $customFields[] = ['field' => (int) $fieldPair->activeCampaignField, 'value' => $fieldPair->customValue]; + } elseif (is_numeric($fieldPair->activeCampaignField)) { + $customFields[] = ['field' => (int) $fieldPair->activeCampaignField, 'value' => $fieldValues[$fieldPair->formField]]; + } else { + $fieldData[$fieldPair->activeCampaignField] = $fieldValues[$fieldPair->formField]; + } + } + } + + if (!empty($customFields)) { + $fieldData['fieldValues'] = $customFields; + } + $activeCampaign['contact'] = (object) $fieldData; + $existContact = $this->existContact($activeCampaign['contact']->email); + + $type = 'notSet'; + $updateContact = $actions->update; + if (!$updateContact && empty($existContact->contacts)) { + $recordApiResponse = $this->storeOrModifyRecord('contacts', wp_json_encode($activeCampaign)); + $type = 'insert'; + if (isset($recordApiResponse->contact)) { + $recordApiResponse = ['success' => true, 'id' => $recordApiResponse->contact->id]; + if (isset($listId) && !empty($listId)) { + $data['contactList'] = (object) [ + 'list' => $listId, + 'contact' => $recordApiResponse['id'], + 'status' => 1 + ]; + $this->storeOrModifyRecord('contactLists', wp_json_encode($data)); + } + if (isset($tags) && !empty($tags)) { + foreach ($tags as $tag) { + $data['contactTag'] = (object) [ + 'contact' => $recordApiResponse['id'], + 'tag' => $tag + ]; + $this->storeOrModifyRecord('contactTags', wp_json_encode($data)); + } + } + if (isset($integrationDetails->selectedAccount) && !empty($integrationDetails->selectedAccount)) { + $data['accountContact'] = [ + 'account' => $listId, + 'contact' => $recordApiResponse['id'], + ]; + if (isset($integrationDetails->job_title)) { + $data['accountContact'] += ['jobTitle' => $integrationDetails->job_title]; + } + $this->storeOrModifyRecord('accountContacts', wp_json_encode((object) $data)); + } + } + } elseif ($updateContact && !empty($existContact->contacts)) { + $recordApiResponse = $this->updateRecord($existContact->contacts[0]->id, $activeCampaign, $existContact); + if (isset($tags) && !empty($tags)) { + foreach ($tags as $tag) { + $data['contactTag'] = (object) [ + 'contact' => $recordApiResponse->contact->id, + 'tag' => $tag + ]; + $this->storeOrModifyRecord('contactTags', wp_json_encode($data)); + } + } + if (isset($recordApiResponse->contact)) { + $recordApiResponse = ['success' => true, 'id' => $recordApiResponse->contact->id]; + } + if (isset($integrationDetails->selectedAccount) && !empty($integrationDetails->selectedAccount)) { + $data['accountContact'] = [ + 'account' => $listId, + 'contact' => $recordApiResponse->contact->id, + ]; + if (isset($integrationDetails->job_title)) { + $data['accountContact'] += ['jobTitle' => $integrationDetails->job_title]; + } + $this->storeOrModifyRecord('accountContacts', wp_json_encode((object) $data)); + } + $type = 'update'; + } elseif ($updateContact && empty($existContact->contacts)) { + $recordApiResponse = $this->storeOrModifyRecord('contacts', wp_json_encode($activeCampaign)); + $type = 'insert'; + if (isset($recordApiResponse->contact)) { + $recordApiResponse = ['success' => true, 'id' => $recordApiResponse->contact->id]; + if (isset($listId) && !empty($listId)) { + $data['contactList'] = (object) [ + 'list' => $listId, + 'contact' => $recordApiResponse['id'], + 'status' => 1 + ]; + $this->storeOrModifyRecord('contactLists', wp_json_encode($data)); + } + if (isset($tags) && !empty($tags)) { + foreach ($tags as $tag) { + $data['contactTag'] = (object) [ + 'contact' => $recordApiResponse['id'], + 'tag' => $tag + ]; + $this->storeOrModifyRecord('contactTags', wp_json_encode($data)); + } + } + if (isset($integrationDetails->selectedAccount) && !empty($integrationDetails->selectedAccount)) { + $data['accountContact'] = [ + 'account' => $listId, + 'contact' => $recordApiResponse['id'], + ]; + if (isset($integrationDetails->job_title)) { + $data['accountContact'] += ['jobTitle' => $integrationDetails->job_title]; + } + $this->storeOrModifyRecord('accountContacts', wp_json_encode((object) $data)); + } + } + } + + if ($type === 'notSet') { + LogHandler::save($this->_integrationID, ['type' => 'record', 'type_name' => 'insert'], 'error', __('Email already exist', 'bit-integrations')); + + return false; + } + if ($recordApiResponse && isset($recordApiResponse->errors)) { + LogHandler::save($this->_integrationID, ['type' => 'record', 'type_name' => $type], 'error', $recordApiResponse->errors); + } else { + LogHandler::save($this->_integrationID, ['type' => 'record', 'type_name' => $type], 'success', $recordApiResponse); + } + + return $recordApiResponse; + } + + private function existContact($email) + { + $searchEndPoint = "{$this->_apiEndpoint}/contacts?email={$email}"; + + return HttpHelper::get($searchEndPoint, null, $this->_defaultHeader); + } +} diff --git a/backend/Actions/ActiveCampaign/Routes.php b/backend/Actions/ActiveCampaign/Routes.php new file mode 100644 index 000000000..b5e496266 --- /dev/null +++ b/backend/Actions/ActiveCampaign/Routes.php @@ -0,0 +1,14 @@ +_integrationDetails = $integrationDetails; + $this->_integrationID = $integId; + } + + public function addSubscriber($auth_token, $listId, $finalData, $doubleOptin) + { + $apiEndpoints = 'https://acumbamail.com/api/1/addSubscriber/'; + $header = [ + 'Content-Type' => 'application/x-www-form-urlencoded' + ]; + + $requestParams = [ + 'auth_token' => $auth_token, + 'list_id' => $listId, + 'welcome_email' => 1, + 'update_subscriber' => 1, + 'merge_fields' => $finalData, + 'double_optin' => $doubleOptin ? 1 : 0, + + ]; + + return HttpHelper::post($apiEndpoints, $requestParams, $header); + } + + public function deleteSubscriber($auth_token, $listId, $finalData) + { + $apiEndpoints = 'https://acumbamail.com/api/1/deleteSubscriber/'; + + $header = [ + 'Content-Type' => 'application/x-www-form-urlencoded' + ]; + + $requestParams = [ + 'auth_token' => $auth_token, + 'list_id' => $listId, + 'email' => $finalData['email'], + ]; + + return HttpHelper::post($apiEndpoints, $requestParams, $header); + } + + public function generateReqDataFromFieldMap($data, $fieldMap) + { + $dataFinal = []; + + foreach ($fieldMap as $key => $value) { + $triggerValue = $value->formField; + $actionValue = $value->acumbamailFormField; + if ($triggerValue === 'custom') { + $dataFinal[$actionValue] = Common::replaceFieldWithValue($value->customValue, $data); + } elseif (!\is_null($data[$triggerValue])) { + $dataFinal[$actionValue] = $data[$triggerValue]; + } + } + + return $dataFinal; + } + + public function execute( + $listId, + $mainAction, + $fieldValues, + $fieldMap, + $auth_token, + $doubleOptin + ) { + $fieldData = []; + $finalData = $this->generateReqDataFromFieldMap($fieldValues, $fieldMap); + if ($mainAction === '1') { + $apiResponse = $this->addSubscriber($auth_token, $listId, $finalData, $doubleOptin); + } elseif ($mainAction === '2') { + $apiResponse = $this->deleteSubscriber($auth_token, $listId, $finalData); + } + if (property_exists($apiResponse, 'error')) { + LogHandler::save($this->_integrationID, wp_json_encode(['type' => 'contact', 'type_name' => 'add-contact']), 'error', wp_json_encode($apiResponse)); + } else { + LogHandler::save($this->_integrationID, wp_json_encode(['type' => 'record', 'type_name' => 'add-contact']), 'success', wp_json_encode($apiResponse)); + } + + return $apiResponse; + } +} diff --git a/backend/Actions/Acumbamail/Routes.php b/backend/Actions/Acumbamail/Routes.php new file mode 100644 index 000000000..3abc1e6fe --- /dev/null +++ b/backend/Actions/Acumbamail/Routes.php @@ -0,0 +1,12 @@ +_integrationID = $integrationID; + // } + + public static function pluginActive($option = null) + { + if (is_plugin_active('affiliate-wp/affiliate-wp.php')) { + return $option === 'get_name' ? 'affiliate-wp/affiliate-wp.php' : true; + } + + 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'); + $cache_group = Config::VAR_PREFIX; + $affiliates = wp_cache_get($cache_key, $cache_group); + + if (false !== $affiliates) { + return $affiliates; + } + + $affiliates = []; + + global $wpdb; + $affiliate_table = esc_sql($wpdb->prefix . 'affiliate_wp_affiliates'); + // phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.PreparedSQL.NotPrepared + $affiliatesIds = $wpdb->get_results( + 'SELECT affiliate_Id FROM ' . $affiliate_table + ); + // phpcs:enable + + foreach ($affiliatesIds as $val) { + $affiliates[] = [ + 'affiliate_id' => $val->affiliate_Id, + 'affiliate_name' => affwp_get_affiliate_name($val->affiliate_Id), + ]; + } + + wp_cache_set($cache_key, $affiliates, $cache_group, 10 * MINUTE_IN_SECONDS); + + return $affiliates; + } + + public function execute($integrationData, $fieldValues) + { + $integrationDetails = $integrationData->flow_details; + $integId = $integrationData->id; + $mainAction = $integrationDetails->mainAction; + $fieldMap = $integrationDetails->field_map; + if ( + empty($integId) + || empty($mainAction) || empty($fieldMap) + ) { + // translators: %s: Integration name + return new WP_Error('REQ_FIELD_EMPTY', wp_sprintf(__('module, fields are required for %s api', 'bit-integrations'), 'Affiliate api')); + } + $recordApiHelper = new RecordApiHelper($integrationDetails, $integId); + $affiliateApiResponse = $recordApiHelper->execute( + $mainAction, + $fieldValues, + $integrationDetails, + $integrationData + ); + + if (is_wp_error($affiliateApiResponse)) { + return $affiliateApiResponse; + } + + return $affiliateApiResponse; + } +} diff --git a/backend/Actions/Affiliate/RecordApiHelper.php b/backend/Actions/Affiliate/RecordApiHelper.php new file mode 100644 index 000000000..ece4b0b34 --- /dev/null +++ b/backend/Actions/Affiliate/RecordApiHelper.php @@ -0,0 +1,205 @@ +_integrationDetails = $integrationDetails; + self::$integrationID = $integId; + } + + public static function getIntegrationId() + { + return $integrationID = self::$integrationID; + } + + public function getAssignmentList() + { + return $assignment_list = $this->assignment_list; + } + + public function generateReqDataFromFieldMap($data, $fieldMap) + { + $dataFinal = []; + + foreach ($fieldMap as $key => $value) { + $triggerValue = $value->formField; + $actionValue = $value->affiliateFormField; + if ($triggerValue === 'custom') { + $dataFinal[$actionValue] = Common::replaceFieldWithValue($value->customValue, $data); + } elseif (!\is_null($data[$triggerValue])) { + $dataFinal[$actionValue] = $data[$triggerValue]; + } + } + + return $dataFinal; + } + + public static function createAffiliateWithSpecificId($affiliateId, $statusId, $referralId, $finalData) + { + switch ($statusId) { + case '1': + $status = 'paid'; + + break; + case '2': + $status = 'unpaid'; + + break; + case '3': + $status = 'pending'; + + break; + case '4': + $status = 'reject'; + + break; + } + switch ($referralId) { + case '1': + $referral = 'sale'; + + break; + case '2': + $referral = 'opt-in'; + + break; + case '3': + $referral = 'lead'; + + break; + } + + $user_id = get_current_user_id(); + $user = get_user_by('id', $user_id); + $finalData['user_id'] = $user_id; + $finalData['type'] = $referral; + $finalData['status'] = $status; + $finalData['custom'] = 'this is custom field'; + $finalData['user_name'] = $user->user_login; + $finalData['affiliate_id'] = $affiliateId; + + $affiliate_user_id = affwp_get_affiliate_user_id($affiliateId); + if ($affiliate_user_id && affwp_is_affiliate($affiliate_user_id)) { + return affwp_add_referral($finalData); + } + LogHandler::save(self::$integrationID, wp_json_encode(['type' => 'group', 'type_name' => 'create-referral']), 'error', wp_json_encode('User are not affiliate')); + } + + public static function createAffiliateFortheUser($statusId, $referralId, $finalData) + { + switch ($statusId) { + case '1': + $status = 'paid'; + + break; + case '2': + $status = 'unpaid'; + + break; + case '3': + $status = 'pending'; + + break; + case '4': + $status = 'reject'; + + break; + } + switch ($referralId) { + case '1': + $referral = 'sale'; + + break; + case '2': + $referral = 'opt-in'; + + break; + case '3': + $referral = 'lead'; + + break; + } + + $user_id = get_current_user_id(); + $affiliateId = affwp_get_affiliate_id($user_id); + $user = get_user_by('id', $user_id); + $finalData['user_id'] = $user_id; + $finalData['type'] = $referral; + $finalData['status'] = $status; + $finalData['custom'] = 'this is custom field'; + $finalData['user_name'] = $user->user_login; + $finalData['affiliate_id'] = $affiliateId; + + $affiliate_user_id = affwp_get_affiliate_user_id($affiliateId); + if ($affiliate_user_id && affwp_is_affiliate($affiliate_user_id)) { + return affwp_add_referral($finalData); + } + LogHandler::save(self::$integrationID, wp_json_encode(['type' => 'referral', 'type_name' => 'create-referral']), 'error', wp_json_encode(__('User are not affiliate', 'bit-integrations'))); + } + + public function execute( + $mainAction, + $fieldValues, + $integrationDetails, + $integrationData + ) { + $fieldData = []; + if ($mainAction === '1') { + $fieldMap = $integrationDetails->field_map; + $finalData = $this->generateReqDataFromFieldMap($fieldValues, $fieldMap); + $affiliateId = $integrationDetails->affiliate_id; + $referralId = $integrationDetails->referralId; + $statusId = $integrationDetails->statusId; + $apiResponse = self::createAffiliateWithSpecificId( + $affiliateId, + $statusId, + $referralId, + $finalData + ); + if ($apiResponse !== 0) { + // translators: %s: Placeholder value + LogHandler::save(self::$integrationID, wp_json_encode(['type' => 'referral', 'type_name' => 'create-referral']), 'success', wp_json_encode(wp_sprintf(__('Created Referral id %s', 'bit-integrations'), $apiResponse))); + } else { + LogHandler::save(self::$integrationID, wp_json_encode(['type' => 'referral', 'type_name' => 'create-referral']), 'error', wp_json_encode(__('Error in creating referral', 'bit-integrations'))); + } + + return $apiResponse; + } + if ($mainAction === '2') { + $fieldMap = $integrationDetails->field_map; + $finalData = $this->generateReqDataFromFieldMap($fieldValues, $fieldMap); + $referralId = $integrationDetails->referralId; + $statusId = $integrationDetails->statusId; + $apiResponse = self::createAffiliateFortheUser( + $statusId, + $referralId, + $finalData + ); + if ($apiResponse !== 0) { + // translators: %s: Placeholder value + LogHandler::save(self::$integrationID, wp_json_encode(['type' => 'referral', 'type_name' => 'create-referral']), 'success', wp_json_encode(wp_sprintf(__('Created Referral id %s', 'bit-integrations'), $apiResponse))); + } else { + LogHandler::save(self::$integrationID, wp_json_encode(['type' => 'referral', 'type_name' => 'create-referral']), 'error', wp_json_encode(__('Error in creating referral', 'bit-integrations'))); + } + + return $apiResponse; + } + } +} diff --git a/backend/Actions/Affiliate/Routes.php b/backend/Actions/Affiliate/Routes.php new file mode 100644 index 000000000..4f72f66f6 --- /dev/null +++ b/backend/Actions/Affiliate/Routes.php @@ -0,0 +1,11 @@ +actionName; if (empty($fieldMap) || empty($authToken) || empty($actionName)) { + // translators: %s: Placeholder value return new WP_Error('REQ_FIELD_EMPTY', wp_sprintf(__('module, fields are required for %s api', 'bit-integrations'), 'Agiled CRM')); } diff --git a/backend/Actions/AgiledCRM/RecordApiHelper.php b/backend/Actions/AgiledCRM/RecordApiHelper.php new file mode 100644 index 000000000..c72d73f26 --- /dev/null +++ b/backend/Actions/AgiledCRM/RecordApiHelper.php @@ -0,0 +1,218 @@ +integrationDetails = $integrationDetails; + $this->integrationId = $integId; + $this->authToken = $integrationDetails->auth_token; + $this->defaultHeader = [ + 'Brand' => $integrationDetails->brand, + 'Content-Type' => 'application/json' + ]; + } + + public function addAccount($finalData) + { + if (empty($finalData['name'])) { + return ['success' => false, 'message' => __('Required field Name is empty', 'bit-integrations'), 'code' => 400]; + } + + $apiEndpoint = "https://my.agiled.app/api/v1/accounts?api_token={$this->authToken}"; + + $staticFieldsKeys = ['name', 'description', 'size', 'email', 'phone', 'website', 'facebook', 'linkedin', 'twitter', 'skype', 'note', 'tax_no']; + + foreach ($finalData as $key => $value) { + if (\in_array($key, $staticFieldsKeys)) { + $requestParams[$key] = $value; + } else { + $requestParams['custom_fields'][] = (object) [ + 'key' => $key, + 'value' => $value + ]; + } + } + + if ($this->integrationDetails->actions->owner) { + $requestParams['owner_id'] = $this->integrationDetails->selectedOwner; + } + + $this->type = 'Account'; + $this->typeName = 'Account created'; + + return HttpHelper::post($apiEndpoint, wp_json_encode($requestParams), $this->defaultHeader); + } + + public function addContact($finalData) + { + if (empty($finalData['first_name']) || empty($finalData['email'])) { + return ['success' => false, 'message' => __('Required field Name or Email is empty', 'bit-integrations'), 'code' => 400]; + } + + $apiEndpoint = "https://my.agiled.app/api/v1/contacts?api_token={$this->authToken}"; + + $staticFieldsKeys = ['first_name', 'email', 'last_name', 'phone', 'job_title', 'facebook', 'linkedin', 'twitter', 'skype', 'note', 'tax_no', 'last_contacted']; + + foreach ($finalData as $key => $value) { + if (\in_array($key, $staticFieldsKeys)) { + $requestParams[$key] = $value; + } else { + $requestParams['custom_fields'][] = (object) [ + 'key' => $key, + 'value' => $value + ]; + } + } + + if (isset($this->integrationDetails->contactRole)) { + $requestParams['role'] = $this->integrationDetails->contactRole; + } + + if ($this->integrationDetails->actions->account) { + $requestParams['account_id'] = $this->integrationDetails->selectedAccount; + } + + if ($this->integrationDetails->actions->owner) { + $requestParams['owner_id'] = (int) $this->integrationDetails->selectedOwner; + } + + if ($this->integrationDetails->actions->source) { + $requestParams['source_id'] = (int) $this->integrationDetails->selectedSource; + } + + if ($this->integrationDetails->actions->status) { + $requestParams['status_id'] = (int) $this->integrationDetails->selectedStatus; + } + + if ($this->integrationDetails->actions->lifeCycleStage) { + $requestParams['life_cycle_stage'] = $this->integrationDetails->selectedLifeCycleStage; + } + + if ($this->integrationDetails->actions->followUp) { + $requestParams['next_follow_up'] = $this->integrationDetails->selectedFollowUp; + } + + $this->type = 'Contact'; + $this->typeName = 'Contact created'; + + return HttpHelper::post($apiEndpoint, wp_json_encode($requestParams), $this->defaultHeader); + } + + public function addDeal($finalData) + { + if (empty($finalData['deal_name'])) { + return ['success' => false, 'message' => __('Required field deal name is empty', 'bit-integrations'), 'code' => 400]; + } + + $apiEndpoint = "https://my.agiled.app/api/v1/crm/pipeline-deals?api_token={$this->authToken}"; + + $requestParams['pipeline_id'] = (int) $this->integrationDetails->selectedCRMPipeline; + $requestParams['stage_id'] = (int) $this->integrationDetails->selectedCRMPipelineStages; + + foreach ($finalData as $key => $value) { + $requestParams[$key] = $value; + } + + if ($this->integrationDetails->actions->owner) { + $requestParams['deal_owner'] = (int) $this->integrationDetails->selectedOwner; + } + + if ($this->integrationDetails->actions->dealType) { + $requestParams['deal_type'] = $this->integrationDetails->selectedDealType; + } + + $this->type = 'Deal'; + $this->typeName = 'Deal created'; + + return HttpHelper::post($apiEndpoint, wp_json_encode($requestParams), $this->defaultHeader); + } + + public function generateReqDataFromFieldMap($data, $fieldMap) + { + $dataFinal = []; + foreach ($fieldMap as $value) { + $triggerValue = $value->formField; + $actionValue = $value->agiledFormField; + if ($triggerValue === 'custom') { + if ($actionValue === 'customFieldKey') { + $dataFinal[$value->customFieldKey] = Common::replaceFieldWithValue($value->customValue, $data); + } else { + $dataFinal[$actionValue] = Common::replaceFieldWithValue($value->customValue, $data); + } + } elseif (!\is_null($data[$triggerValue])) { + if ($actionValue === 'customFieldKey') { + $dataFinal[$value->customFieldKey] = $data[$triggerValue]; + } else { + $dataFinal[$actionValue] = $data[$triggerValue]; + } + } + } + + return $dataFinal; + } + + public function generateDealFieldMap($data, $fieldMap) + { + $dataFinal = []; + foreach ($fieldMap as $value) { + $triggerValue = $value->formField; + $actionValue = $value->agiledFormField; + if ($triggerValue === 'custom') { + $dataFinal[$actionValue] = $value->customValue; + } elseif (!\is_null($data[$triggerValue])) { + $dataFinal[$actionValue] = $data[$triggerValue]; + } + } + + return $dataFinal; + } + + public function execute($fieldValues, $fieldMap, $actionName) + { + if ($actionName === 'account') { + $finalData = $this->generateReqDataFromFieldMap($fieldValues, $fieldMap); + $apiResponse = $this->addAccount($finalData); + } elseif ($actionName === 'contact') { + $finalData = $this->generateReqDataFromFieldMap($fieldValues, $fieldMap); + $apiResponse = $this->addContact($finalData); + } elseif ($actionName === 'deal') { + $finalData = $this->generateDealFieldMap($fieldValues, $fieldMap); + $apiResponse = $this->addDeal($finalData); + } + + if ($apiResponse->data->id || $apiResponse->status === 'success') { + $res = [$this->typeName . ' successfully']; + LogHandler::save($this->integrationId, wp_json_encode(['type' => $this->type, 'type_name' => $this->typeName]), 'success', wp_json_encode($res)); + } else { + LogHandler::save($this->integrationId, wp_json_encode(['type' => $this->type, 'type_name' => $this->type . ' creating']), 'error', wp_json_encode($apiResponse)); + } + + return $apiResponse; + } +} diff --git a/backend/Actions/AgiledCRM/Routes.php b/backend/Actions/AgiledCRM/Routes.php new file mode 100644 index 000000000..7d60da9fa --- /dev/null +++ b/backend/Actions/AgiledCRM/Routes.php @@ -0,0 +1,17 @@ +integrationDetails = $integrationDetails; + $this->integrationID = $integId; + $this->defaultHeader = [ + 'Authorization' => 'Bearer ' . $integrationDetails->auth_token, + 'Content-Type' => 'application/json' + ]; + } + + public function createRecord($finalData) + { + $baseId = $this->integrationDetails->selectedBase; + $tableId = $this->integrationDetails->selectedTable; + $apiEndpoint = "https://api.airtable.com/v0/{$baseId}/{$tableId}"; + + $floatTypeFields = ['currency', 'number', 'percent']; + $intTypefields = ['duration', 'rating']; + + foreach ($finalData as $key => $value) { + $keyTypes = explode('{btcbi}', $key); + $fieldId = $keyTypes[0]; + $fieldType = $keyTypes[1]; + + if (\in_array($fieldType, $floatTypeFields)) { + $fields[$fieldId] = (float) $value; + } elseif (\in_array($fieldType, $intTypefields)) { + $fields[$fieldId] = (int) $value; + } elseif ($fieldType === 'barcode') { + $fields[$fieldId] = (object) ['text' => $value]; + } elseif ($fieldType === 'multipleAttachments') { + $fields[$fieldId] = static::parseAttachmentFile([$value]); + } else { + $fields[$fieldId] = $value; + } + } + + $data['records'][] = (object) [ + 'fields' => (object) $fields + ]; + + return HttpHelper::post($apiEndpoint, wp_json_encode($data), $this->defaultHeader); + } + + public function generateReqDataFromFieldMap($data, $fieldMap) + { + $dataFinal = []; + foreach ($fieldMap as $value) { + $triggerValue = $value->formField; + $actionValue = $value->airtableFormField; + if ($triggerValue === 'custom') { + $dataFinal[$actionValue] = Common::replaceFieldWithValue($value->customValue, $data); + } elseif (!\is_null($data[$triggerValue])) { + $dataFinal[$actionValue] = $data[$triggerValue]; + } + } + + return $dataFinal; + } + + public function execute($fieldValues, $fieldMap) + { + $finalData = $this->generateReqDataFromFieldMap($fieldValues, $fieldMap); + $apiResponse = $this->createRecord($finalData); + + if (isset($apiResponse->records)) { + $successMessage = ['message' => __('Record created successfully', 'bit-integrations')]; + LogHandler::save($this->integrationID, wp_json_encode(['type' => 'record', 'type_name' => 'Record created']), 'success', wp_json_encode($successMessage)); + + if (isset($apiResponse->details) && $apiResponse->details->message == 'partialSuccess') { + LogHandler::save($this->integrationID, wp_json_encode(['type' => 'record', 'type_name' => 'Creating record']), 'error', wp_json_encode($apiResponse->details)); + } + } else { + LogHandler::save($this->integrationID, wp_json_encode(['type' => 'record', 'type_name' => 'Creating record']), 'error', wp_json_encode($apiResponse)); + } + + return $apiResponse; + } + + private static function parseAttachmentFile(array $files) + { + $allFiles = []; + + foreach ($files as $file) { + if (\is_array($file)) { + $allFiles = static::parseAttachmentFile($file); + } else { + $allFiles[] = (object) ['url' => Common::fileUrl($file)]; + } + } + + return $allFiles; + } +} diff --git a/backend/Actions/Airtable/Routes.php b/backend/Actions/Airtable/Routes.php new file mode 100644 index 000000000..5993a390b --- /dev/null +++ b/backend/Actions/Airtable/Routes.php @@ -0,0 +1,12 @@ +actionName; if (empty($fieldMap) || empty($authToken) || empty($actionName)) { + // translators: %s: Placeholder value return new WP_Error('REQ_FIELD_EMPTY', wp_sprintf(__('module, fields are required for %s api', 'bit-integrations'), 'Asana')); } diff --git a/backend/Actions/Asana/RecordApiHelper.php b/backend/Actions/Asana/RecordApiHelper.php new file mode 100644 index 000000000..4f509048c --- /dev/null +++ b/backend/Actions/Asana/RecordApiHelper.php @@ -0,0 +1,115 @@ +integrationDetails = $integrationDetails; + $this->integrationId = $integId; + $this->apiUrl = 'https://app.asana.com/api/1.0/'; + $this->defaultHeader = [ + 'Authorization' => 'Bearer ' . $integrationDetails->api_key, + 'content-type' => 'application/json' + ]; + } + + public function addTask($finalData) + { + if (!isset($finalData['name'])) { + return ['success' => false, 'message' => __('Required field task name is empty', 'bit-integrations'), 'code' => 400]; + } + $staticFieldsKeys = ['name', 'due_at', 'due_on', 'notes']; + $customFields = []; + foreach ($finalData as $key => $value) { + if (\in_array($key, $staticFieldsKeys)) { + $requestParams[$key] = $value; + } else { + $customFields[$key] = $value; + } + } + + if (!empty($this->integrationDetails->selectedProject)) { + $requestParams['projects'][] = ($this->integrationDetails->selectedProject); + } + if (\count($customFields)) { + $requestParams['custom_fields'] = $customFields; + } + + $this->type = 'Task'; + $this->typeName = 'Task created'; + + $apiEndpoint = $this->apiUrl . 'tasks'; + + $response = HttpHelper::post($apiEndpoint, wp_json_encode(['data' => $requestParams]), $this->defaultHeader); + if (!isset($this->integrationDetails->selectedSections)) { + return $response; + } + if (isset($response->data)) { + return $this->addTaskToSection($response->data->gid, $this->integrationDetails->selectedSections); + } + } + + public function addTaskToSection($taskId, $sectionId) + { + $apiEndpoint = $this->apiUrl . 'sections/' . $sectionId . '/addTask'; + $requestParams['task'] = $taskId; + + return HttpHelper::post($apiEndpoint, wp_json_encode(['data' => $requestParams]), $this->defaultHeader); + } + + public function generateReqDataFromFieldMap($data, $fieldMap) + { + $dataFinal = []; + foreach ($fieldMap as $value) { + $triggerValue = $value->formField; + $actionValue = $value->asanaFormField; + + $fieldKey = $actionValue === 'fields' ? $value->customFieldKey : $actionValue; + $dataFinal[$fieldKey] = $triggerValue === 'custom' && isset($value->customValue) ? Common::replaceFieldWithValue($value->customValue, $data) : $data[$triggerValue] ?? null; + } + + return $dataFinal; + } + + public function execute($fieldValues, $fieldMap, $actionName) + { + $finalData = $this->generateReqDataFromFieldMap($fieldValues, $fieldMap); + if ($actionName === 'task') { + $apiResponse = $this->addTask($finalData); + } + + if ($apiResponse->data || $apiResponse->status === 'success') { + $res = [$this->typeName . ' successfully']; + LogHandler::save($this->integrationId, wp_json_encode(['type' => $this->type, 'type_name' => $this->typeName]), 'success', wp_json_encode($res)); + } else { + LogHandler::save($this->integrationId, wp_json_encode(['type' => $this->type, 'type_name' => $this->type . ' creating']), 'error', wp_json_encode($apiResponse)); + } + + return $apiResponse; + } +} diff --git a/backend/Actions/Asana/Routes.php b/backend/Actions/Asana/Routes.php new file mode 100644 index 000000000..34ac3470b --- /dev/null +++ b/backend/Actions/Asana/Routes.php @@ -0,0 +1,14 @@ +_integrationID = $integrationId; + } + + public function insertRecord($data, $actions, $lists, $tags) + { + $contact_obj = BWF_Contacts::get_instance(); + $contact = $contact_obj->get_contact_by('email', $data['email']); + $userExist = (absint($contact->get_id()) > 0); + + if ($userExist && isset($actions->skip_if_exists) && $actions->skip_if_exists) { + $response = ['success' => false, 'messages' => __('Contact already exists!', 'bit-integrations')]; + } else { + foreach ($data as $key => $item) { + $obj = 'set_' . $key; + $contact->{$obj}($item); + } + $contact->set_status(1); + $contact->save(); + + static::addTags($data['email'], $tags); + static::addLists($data['email'], $lists); + $customContact = new BWFCRM_Contact($data['email']); + foreach ($data as $key => $item) { + if ($key == 'address') { + $key = 'address-1'; + } + $customContact->set_field_by_slug($key, $item); + } + $customContact->save_fields(); + + if (absint($contact->get_id()) > 0) { + $response = ['success' => true, 'messages' => __('Insert successfully!', 'bit-integrations')]; + } else { + $response = ['success' => false, 'messages' => __('Something wrong!', 'bit-integrations')]; + } + } + + return $response; + } + + public function execute($fieldValues, $fieldMap, $actions, $lists, $tags) + { + $fieldData = []; + foreach ($fieldMap as $fieldPair) { + if (!empty($fieldPair->autonamiField)) { + if ($fieldPair->formField === 'custom' && isset($fieldPair->customValue)) { + $fieldData[$fieldPair->autonamiField] = $fieldPair->customValue; + } else { + $fieldData[$fieldPair->autonamiField] = \is_array($fieldValues[$fieldPair->formField]) ? wp_json_encode($fieldValues[$fieldPair->formField]) : $fieldValues[$fieldPair->formField]; + } + } + } + + $recordApiResponse = $this->insertRecord($fieldData, $actions, $lists, $tags); + + if ($recordApiResponse['success']) { + LogHandler::save($this->_integrationID, ['type' => 'record', 'type_name' => 'insert'], 'success', $recordApiResponse); + } else { + LogHandler::save($this->_integrationID, ['type' => 'record', 'type_name' => 'insert'], 'error', $recordApiResponse); + } + + return $recordApiResponse; + } + + private static function addTags($email, $tags) + { + $customContact = new BWFCRM_Contact($email); + foreach ($tags as $tag_id) { + $tags_to_add[] = ['id' => $tag_id]; + } + + $customContact->add_tags($tags_to_add); + } + + private static function addLists($email, $lists) + { + $customContact = new BWFCRM_Contact($email); + foreach ($lists as $list_id) { + $lists_to_add[] = ['id' => $list_id]; + } + + $customContact->add_lists($lists_to_add); + } +} diff --git a/backend/Actions/Autonami/Routes.php b/backend/Actions/Autonami/Routes.php new file mode 100644 index 000000000..11e1136b2 --- /dev/null +++ b/backend/Actions/Autonami/Routes.php @@ -0,0 +1,12 @@ +_integrationID); diff --git a/backend/Actions/BenchMark/RecordApiHelper.php b/backend/Actions/BenchMark/RecordApiHelper.php new file mode 100644 index 000000000..6c5bbc463 --- /dev/null +++ b/backend/Actions/BenchMark/RecordApiHelper.php @@ -0,0 +1,175 @@ +_defaultHeader = $api_secret; + $this->_integrationID = $integId; + } + + public function storeOrModifyRecord($method, $listId, $data) + { + $apiEndpoint = "https://clientapi.benchmarkemail.com/Contact/{$listId}/ContactDetails"; + + $body = ['Data' => static::dataMapping($data)]; + $headers = [ + 'AuthToken' => $this->_defaultHeader, + 'Content-Type' => 'application/json' + ]; + + return HttpHelper::post($apiEndpoint, wp_json_encode($body), $headers); + } + + public function updateRecord($data, $existContact) + { + $id = $existContact->Response->Data[0]->ID; + $listId = $existContact->Response->Data[0]->ContactMasterID; + + $updateRecordEndpoint = "https://clientapi.benchmarkemail.com/Contact/{$listId}/ContactDetails/{$id}"; + + $body = ['Data' => static::dataMapping($data)]; + $headers = [ + 'AuthToken' => $this->_defaultHeader, + 'Content-Type' => 'application/json' + ]; + + return HttpHelper::request($updateRecordEndpoint, 'PATCH', wp_json_encode($body), $headers); + } + + public function execute($fieldValues, $fieldMap, $actions, $listId) + { + $fieldData = []; + $customFields = []; + + foreach ($fieldMap as $fieldKey => $fieldPair) { + if (!empty($fieldPair->benchMarkField)) { + if ($fieldPair->formField === 'custom' && isset($fieldPair->customValue) && !is_numeric($fieldPair->benchMarkField)) { + $fieldData[$fieldPair->benchMarkField] = $fieldPair->customValue; + } elseif (is_numeric($fieldPair->benchMarkField) && $fieldPair->formField === 'custom' && isset($fieldPair->customValue)) { + $customFields[] = ['field' => (int) $fieldPair->benchMarkField, 'value' => $fieldPair->customValue]; + } elseif (is_numeric($fieldPair->benchMarkField)) { + $customFields[] = ['field' => (int) $fieldPair->benchMarkField, 'value' => $fieldValues[$fieldPair->formField]]; + } else { + $fieldData[$fieldPair->benchMarkField] = $fieldValues[$fieldPair->formField]; + } + } + } + + if (!empty($customFields)) { + $fieldData['fieldValues'] = $customFields; + } + $benchMark = (object) $fieldData; + + $existContact = $this->existContact($benchMark->email); + + if (($existContact->Response->Count == 0) || ($existContact->Response->Count == null)) { + $recordApiResponse = $this->storeOrModifyRecord('Contact', $listId, $benchMark); + + $type = 'insert'; + } else { + if ($actions->update == 'true') { + $this->updateRecord($benchMark, $existContact); + $type = 'update'; + } else { + LogHandler::save($this->_integrationID, ['type' => 'record', 'type_name' => 'insert'], 'error', __('Email address already exists in the system', 'bit-integrations')); + + wp_send_json_error( + __('Email address already exists in the system', 'bit-integrations'), + 400 + ); + } + } + + if (($recordApiResponse && isset($recordApiResponse->errors))) { + LogHandler::save($this->_integrationID, ['type' => 'record', 'type_name' => $type], 'error', $recordApiResponse->errors); + } else { + LogHandler::save($this->_integrationID, ['type' => 'record', 'type_name' => $type], 'success', $recordApiResponse); + } + + return $recordApiResponse; + } + + // Check if a contact exists through email. + private function existContact($email) + { + $queries = http_build_query([ + 'Search' => $email, + ]); + + $apiEndpoint = 'https://clientapi.benchmarkemail.com/Contact/ContactDetails?' . $queries; + + $authorizationHeader['AuthToken'] = $this->_defaultHeader; + + return HttpHelper::get($apiEndpoint, null, $authorizationHeader); + } + + private static function dataMapping($data) + { + $fieldsMapping = [ + 'Email' => ['email', 'Email'], + 'FirstName' => ['firstname', 'FirstName'], + 'MiddleName' => ['middlename', 'MiddleName'], + 'LastName' => ['lastname', 'LastName'], + 'Field1' => ['address', 'Field1'], + 'Field2' => ['city', 'Field2'], + 'Field3' => ['state', 'Field3'], + 'Field4' => ['zip', 'Field4'], + 'Field5' => ['country', 'Field5'], + 'Field6' => ['phone', 'Field6'], + 'Field7' => ['fax', 'Field7'], + 'Field8' => ['cell_phone', 'Field8'], + 'Field9' => ['company_name', 'Field9'], + 'Field10' => ['job_title', 'Field10'], + 'Field11' => ['business_phone', 'Field11'], + 'Field12' => ['business_fax', 'Field12'], + 'Field13' => ['business_address', 'Field13'], + 'Field14' => ['business_city', 'Field14'], + 'Field15' => ['business_state', 'Field15'], + 'Field16' => ['business_zip', 'Field16'], + 'Field17' => ['business_country', 'Field17'], + 'Field18' => ['notes', 'Field18'], + 'Field19' => ['date_1', 'Field19'], + 'Field20' => ['date_2', 'Field20'], + 'Field21' => ['extra_3', 'Field21'], + 'Field22' => ['extra_4', 'Field22'], + 'Field23' => ['extra_5', 'Field23'], + 'Field24' => ['extra_6', 'Field24'] + ]; + + $fields = []; + foreach ($fieldsMapping as $key => $fieldOptions) { + foreach ($fieldOptions as $field) { + if (isset($data->{$field})) { + $fields[$key] = $data->{$field}; + + break; + } + } + + if (!isset($fields[$key])) { + $fields[$key] = ''; + } + } + + $fields['EmailPerm'] = '1'; + + return $fields; + } +} diff --git a/backend/Actions/BenchMark/Routes.php b/backend/Actions/BenchMark/Routes.php new file mode 100644 index 000000000..aa1b75edf --- /dev/null +++ b/backend/Actions/BenchMark/Routes.php @@ -0,0 +1,12 @@ +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 function getAllFields($fieldsRequestParams) + { + BentoHelper::checkValidation($fieldsRequestParams, $fieldsRequestParams->action ?? ''); + + switch ($fieldsRequestParams->action) { + case 'add_people': + $defaultFields = [(object) ['label' => __('Email Address', 'bit-integrations'), 'key' => 'email', 'required' => true]]; + $fields = Hooks::apply(Config::withPrefix('bento_get_user_fields'), $defaultFields, $fieldsRequestParams); + + /** + * @deprecated 2.7.8 Use `bit_integrations_bento_get_user_fields` filter instead. + * @since 2.7.8 + */ + $fields = Hooks::apply('btcbi_bento_get_user_fields', $fields, $fieldsRequestParams); + + break; + case 'add_event': + $fields = Hooks::apply(Config::withPrefix('bento_get_event_fields'), []); + + /** + * @deprecated 2.7.8 Use `bit_integrations_bento_get_event_fields` filter instead. + * @since 2.7.8 + */ + $fields = Hooks::apply('btcbi_bento_get_event_fields', $fields); + + break; + + default: + $fields = []; + + break; + } + + wp_send_json_success($fields, 200); + } + + public function getAlTags($fieldsRequestParams) + { + BentoHelper::checkValidation($fieldsRequestParams); + + $tags = Hooks::apply(Config::withPrefix('bento_get_all_tags'), [], $fieldsRequestParams); + + /** + * @deprecated 2.7.8 Use `bit_integrations_bento_get_all_tags` filter instead. + * @since 2.7.8 + */ + $tags = Hooks::apply('btcbi_bento_get_all_tags', $tags, $fieldsRequestParams); + + wp_send_json_success($tags, 200); + } + + public function execute($integrationData, $fieldValues) + { + $integrationDetails = $integrationData->flow_details; + $integId = $integrationData->id; + $publishableKey = $integrationDetails->publishable_key; + $secretKey = $integrationDetails->secret_key; + $siteUUID = $integrationDetails->site_uuid; + $fieldMap = $integrationDetails->field_map; + $action = $integrationDetails->action; + + if (empty($fieldMap) || empty($publishableKey) || empty($secretKey) || empty($siteUUID) || empty($action)) { + // translators: %s: Placeholder value + return new WP_Error('REQ_FIELD_EMPTY', wp_sprintf(__('module, fields are required for %s api', 'bit-integrations'), 'Bento')); + } + + $recordApiHelper = new RecordApiHelper( + $integrationDetails, + $integId, + $publishableKey, + $secretKey, + $siteUUID + ); + + return $recordApiHelper->execute($fieldValues, $fieldMap, $action); + } +} diff --git a/includes/Actions/Bento/BentoHelper.php b/backend/Actions/Bento/BentoHelper.php similarity index 93% rename from includes/Actions/Bento/BentoHelper.php rename to backend/Actions/Bento/BentoHelper.php index e569ac3cc..df0af429b 100644 --- a/includes/Actions/Bento/BentoHelper.php +++ b/backend/Actions/Bento/BentoHelper.php @@ -4,9 +4,9 @@ * Bento Record Api */ -namespace BitCode\FI\Actions\Bento; +namespace BitApps\Integrations\Actions\Bento; -use BitCode\FI\Core\Util\HttpHelper; +use BitApps\Integrations\Core\Util\HttpHelper; /** * Provide functionality for Record insert, upsert diff --git a/backend/Actions/Bento/RecordApiHelper.php b/backend/Actions/Bento/RecordApiHelper.php new file mode 100644 index 000000000..43dd84dd5 --- /dev/null +++ b/backend/Actions/Bento/RecordApiHelper.php @@ -0,0 +1,144 @@ +integrationDetails = $integrationDetails; + $this->integrationId = $integId; + $this->siteUUID = $siteUUID; + $this->publishableKey = $publishableKey; + $this->secretKey = $secretKey; + $this->defaultHeader = BentoHelper::setHeaders($publishableKey, $secretKey); + } + + public function addPeople($finalData) + { + if (empty($finalData['email'])) { + return ['success' => false, 'message' => __('Required field Email is empty', 'bit-integrations'), 'code' => 400]; + } + + $email = $finalData['email']; + unset($finalData['email']); + + $apiEndpoint = BentoHelper::setEndpoint('subscribers', $this->siteUUID); + $response = HttpHelper::post($apiEndpoint, wp_json_encode(['email' => $email]), $this->defaultHeader); + + if (!BentoHelper::checkResponseCode()) { + return $response; + } + + $integration = $this->integrationDetails; + $utilities = [ + 'tags' => $integration->selected_tags ?? [], + 'EventTags' => $integration->selected_tags_via_event ?? [], + 'subscribe' => $integration->subscribe ?? false, + ]; + + $reqParams = BentoHelper::setReqParams($this->siteUUID, $this->publishableKey, $this->secretKey); + + Hooks::run(Config::withPrefix('bento_update_user_data'), false, $reqParams, $email, $finalData, $utilities); + + /** + * @deprecated 2.7.8 Use `bit_integrations_bento_update_user_data` action instead. + * @since 2.7.8 + */ + Hooks::run('btcbi_bento_update_user_data', false, $reqParams, $email, $finalData, $utilities); + + return $response; + } + + public function addEvent($finalData) + { + if (empty($finalData['email']) || empty($finalData['type'])) { + return ['success' => false, 'message' => __('Required field Email is empty', 'bit-integrations'), 'code' => 400]; + } + + $reqParams = BentoHelper::setReqParams($this->siteUUID, $this->publishableKey, $this->secretKey); + + $response = Hooks::apply(Config::withPrefix('bento_store_event'), false, $reqParams, $finalData); + + /** + * @deprecated 2.7.8 Use `bit_integrations_bento_store_event` filter instead. + * @since 2.7.8 + */ + $response = Hooks::apply('btcbi_bento_store_event', $response, $reqParams, $finalData); + + // translators: %s: Placeholder value + return empty($response) ? (object) ['error' => wp_sprintf(__('%s plugin is not installed or activate', 'bit-integrations'), 'Bit Integrations Pro')] : $response; + } + + public function generateReqDataFromFieldMap($data, $fieldMap) + { + $dataFinal = []; + foreach ($fieldMap as $value) { + $triggerValue = ($value->formField === 'custom' && !empty($value->customValue)) ? Common::replaceFieldWithValue($value->customValue, $data) : $data[$value->formField]; + $actionValue = $value->bentoFormField === 'customFieldKey' && !empty($value->customFieldKey) ? $value->customFieldKey : $value->bentoFormField; + + $dataFinal[$actionValue] = $triggerValue; + } + + return $dataFinal; + } + + public function execute($fieldValues, $fieldMap, $action) + { + $finalData = $this->generateReqDataFromFieldMap($fieldValues, $fieldMap); + + switch ($action) { + case 'add_people': + $type = 'People'; + $typeName = 'Add People'; + $apiResponse = $this->addPeople($finalData); + $logStatus = (!BentoHelper::checkResponseCode() || empty($apiResponse->data)) ? 'error' : 'success'; + + break; + case 'add_event': + $type = 'Event'; + $typeName = 'Add Event'; + $apiResponse = $this->addEvent($finalData); + $logStatus = (!BentoHelper::checkResponseCode() || empty($apiResponse->results)) ? 'error' : 'success'; + + break; + + default: + break; + } + + LogHandler::save($this->integrationId, wp_json_encode(['type' => $type, 'type_name' => $typeName]), $logStatus, wp_json_encode($apiResponse)); + + return $apiResponse; + } +} diff --git a/backend/Actions/Bento/Routes.php b/backend/Actions/Bento/Routes.php new file mode 100644 index 000000000..764aa9011 --- /dev/null +++ b/backend/Actions/Bento/Routes.php @@ -0,0 +1,12 @@ +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 function bitFormAllFormList($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 function bitFormFetchSingleFormFields($requestParams) + { + if ( + empty($requestParams->app_domain) + || empty($requestParams->api_key) + || empty($requestParams->id) + ) { + 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/fields/' . $requestParams->id; + + $apiResponse = HttpHelper::get($apiEndpoint, null, $authorizationHeader, ['sslverify' => false]); + if ($apiResponse->success) { + wp_send_json_success($apiResponse->fields, 200); + } else { + wp_send_json_error( + 'There is an error .', + 400 + ); + } + } + + public function fetchAllBoards($queryParams) + { + if ( + empty($queryParams->accessToken) + || empty($queryParams->clientId) + ) { + wp_send_json_error( + __( + 'Requested parameter is empty', + 'bit-integrations' + ), + 400 + ); + } + $response = []; + $apiEndpoint = $this->baseUrl . 'members/me?key=' . $queryParams->clientId . '&token=' . $queryParams->accessToken; + $getUserInfoResponse = HttpHelper::get($apiEndpoint, null); + $apiEndpoint = $this->baseUrl . 'members/' . $getUserInfoResponse->username . '/boards?key=' . $queryParams->clientId . '&token=' . $queryParams->accessToken; + $allBoardResponse = HttpHelper::get($apiEndpoint, null); + + $allList = []; + if (!is_wp_error($allBoardResponse) && empty($allBoardResponse->response->error)) { + $boardLists = $allBoardResponse; + foreach ($boardLists as $boardList) { + $allList[] = (object) [ + 'boardId' => $boardList->id, + 'boardName' => $boardList->name + ]; + } + uksort($allList, 'strnatcasecmp'); + $response['allBoardlist'] = $allList; + } else { + wp_send_json_error( + $allBoardResponse->response->error->message, + 400 + ); + } + wp_send_json_success($response, 200); + } + + public function fetchAllLists($queryParams) + { + if ( + empty($queryParams->accessToken) + || empty($queryParams->clientId) + ) { + wp_send_json_error( + __( + 'Requested parameter is empty', + 'bit-integrations' + ), + 400 + ); + } + $response = []; + + $apiEndpoint = $this->baseUrl . 'boards/' . $queryParams->boardId . '/lists?key=' . $queryParams->clientId . '&token=' . $queryParams->accessToken; + $getListsResponse = HttpHelper::get($apiEndpoint, null); + + $allList = []; + if (!is_wp_error($getListsResponse) && empty($getListsResponse->response->error)) { + $singleBoardLists = $getListsResponse; + foreach ($singleBoardLists as $singleBoardList) { + $allList[] = (object) [ + 'listId' => $singleBoardList->id, + 'listName' => $singleBoardList->name + ]; + } + uksort($allList, 'strnatcasecmp'); + $response['alllists'] = $allList; + } else { + wp_send_json_error( + $allBoardResponse->response->error->message, + 400 + ); + } + wp_send_json_success($response, 200); + } + + /** + * Save updated access_token to avoid unnecessary token generation + * + * @param object $integrationData Details of flow + * @param array $fieldValues Data to send Mail Chimp + * + * @return null + */ + public function execute($integrationData, $fieldValues) + { + $integrationDetails = $integrationData->flow_details; + $formId = $integrationDetails->id; + $api_key = $integrationDetails->api_key; + $domainName = $integrationDetails->domainName; + $integId = $integrationData->id; + $fieldMap = $integrationDetails->field_map; + $defaultDataConf = $integrationDetails->default; + + if ( + empty($fieldMap) + || empty($defaultDataConf) + || empty($api_key) + || empty($domainName) + || empty($formId) + ) { + // translators: %s: Placeholder value + return new WP_Error('REQ_FIELD_EMPTY', wp_sprintf(__('module, fields are required for %s api', 'bit-integrations'), 'Bit From')); + } + $recordApiHelper = new RecordApiHelper($integrationDetails, $integId); + $bitFormApiResponse = $recordApiHelper->execute( + $defaultDataConf, + $fieldValues, + $fieldMap, + $api_key, + $domainName, + $formId + ); + + if (is_wp_error($bitFormApiResponse)) { + return $bitFormApiResponse; + } + + return $bitFormApiResponse; + } +} diff --git a/backend/Actions/BitForm/RecordApiHelper.php b/backend/Actions/BitForm/RecordApiHelper.php new file mode 100644 index 000000000..42a51c15a --- /dev/null +++ b/backend/Actions/BitForm/RecordApiHelper.php @@ -0,0 +1,88 @@ +_integrationDetails = $integrationDetails; + $this->_integrationID = $integId; + } + + public function generateReqDataFromFieldMap($data, $fieldMap) + { + $dataFinal = []; + + foreach ($fieldMap as $key => $value) { + $triggerValue = $value->formField; + $actionValue = $value->BitFormMapField; + if ($triggerValue === 'custom') { + $dataFinal[$actionValue] = Common::replaceFieldWithValue($value->customValue, $data); + } elseif (!\is_null($data[$triggerValue])) { + $dataFinal[$actionValue] = $data[$triggerValue]; + } + } + + return $dataFinal; + } + + public function insertRecord($finalData, $api_key, $domainName, $formId) + { + if ( + empty($domainName) + || empty($api_key) + || empty($formId) + ) { + wp_send_json_error( + __( + 'Requested parameter is empty', + 'bit-integrations' + ), + 400 + ); + } + $authorizationHeader = [ + 'Bitform-Api-Key' => $api_key + ]; + + $apiEndpoint = $domainName . '/wp-json/bitform/v1/entry/' . $formId; + + return HttpHelper::post($apiEndpoint, $finalData, $authorizationHeader); + } + + public function execute( + $defaultDataConf, + $fieldValues, + $fieldMap, + $api_key, + $domainName, + $formId + ) { + $fieldData = []; + $finalData = $this->generateReqDataFromFieldMap($fieldValues, $fieldMap); + + $apiResponse = $this->insertRecord($finalData, $api_key, $domainName, $formId); + + if (property_exists($apiResponse, 'errors')) { + LogHandler::save($this->_integrationID, wp_json_encode(['type' => 'contact', 'type_name' => 'add-contact']), 'error', wp_json_encode($apiResponse)); + } else { + LogHandler::save($this->_integrationID, wp_json_encode(['type' => 'record', 'type_name' => 'add-contact']), 'success', wp_json_encode($apiResponse)); + } + + return $apiResponse; + } +} diff --git a/backend/Actions/BitForm/Routes.php b/backend/Actions/BitForm/Routes.php new file mode 100644 index 000000000..027e8ac7d --- /dev/null +++ b/backend/Actions/BitForm/Routes.php @@ -0,0 +1,12 @@ +prefix . 'bp_groups'); + // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.PreparedSQL.NotPrepared -- Reading BuddyBoss groups table directly; static table name. + $groups = $wpdb->get_results('SELECT id, name FROM ' . $groups_table); + wp_cache_set($cache_key, $groups, $cache_group, 10 * MINUTE_IN_SECONDS); + } + + wp_send_json_success($groups, 200); + } + // translators: %s: Plugin name + wp_send_json_error(wp_sprintf(__('%s must be activated!', 'bit-integrations'), 'BuddyBoss')); + } + + public static function getAllUser() + { + include_once ABSPATH . 'wp-admin/includes/plugin.php'; + if (self::pluginActive()) { + global $wpdb; + $cache_key = Config::withPrefix('wp_users_basic'); + $cache_group = Config::VAR_PREFIX; + $users = wp_cache_get($cache_key, $cache_group); + + if (false === $users) { + $users_table = esc_sql($wpdb->users); + // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.PreparedSQL.NotPrepared -- Reading users table directly for BuddyBoss user selection. + $users = $wpdb->get_results('SELECT ID, display_name FROM ' . $users_table); + wp_cache_set($cache_key, $users, $cache_group, 10 * MINUTE_IN_SECONDS); + } + + wp_send_json_success($users, 200); + } + // translators: %s: Plugin name + wp_send_json_error(wp_sprintf(__('%s must be activated!', 'bit-integrations'), 'BuddyBoss')); + } + + public static function getAllForums() + { + $forum_args = [ + 'post_type' => bbp_get_forum_post_type(), + 'posts_per_page' => 999, + 'orderby' => 'title', + 'order' => 'ASC', + 'post_status' => ['publish', 'private'], + ]; + + $forumList = get_posts($forum_args); + + foreach ($forumList as $key => $val) { + $forums[] = [ + 'forum_id' => $val->ID, + 'forum_title' => $val->post_title, + ]; + } + + return $forums; + } + + public static function getAllTopics($requestParams) + { + $forum_id = $requestParams->forumID; + + $topic_args = [ + 'post_type' => bbp_get_topic_post_type(), + 'posts_per_page' => -1, + 'orderby' => 'title', + 'order' => 'ASC', + 'post_parent' => $forum_id, + 'post_status' => 'publish', + ]; + + $topic_list = get_posts($topic_args); + $topics = []; + + foreach ($topic_list as $key => $val) { + $topics[] = [ + 'topic_id' => $val->ID, + 'topic_title' => $val->post_title, + ]; + } + + wp_send_json_success($topics); + } + + // for action 11 - BuddyBoss update started + + public static function registerComponents($component_names, $active_components) + { + $component_names = ! \is_array($component_names) ? [] : $component_names; + $component_names[] = 'bit-integrations'; + + return $component_names; + } + + public static function notificationForUser($content, $item_id, $secondary_item_id, $action_item_count, $format, $component_action_name, $component_name, $id) + { + if ('bit_integrations_send_notification' === $component_action_name) { + $notification_content = bp_notifications_get_meta($id, 'uo_notification_content'); + $notification_link = bp_notifications_get_meta($id, 'uo_notification_link'); + + if ('string' === $format) { + return $notification_content; + } elseif ('object' === $format) { + return [ + 'text' => $notification_content, + 'link' => $notification_link, + ]; + } + } + + return $content; + } + + // end action 11 + + public function execute($integrationData, $fieldValues) + { + $integrationDetails = $integrationData->flow_details; + $integId = $integrationData->id; + $mainAction = $integrationDetails->mainAction; + $fieldMap = $integrationDetails->field_map; + if ( + empty($integId) + || empty($mainAction) + ) { + // translators: %s: Integration name + return new WP_Error('REQ_FIELD_EMPTY', wp_sprintf(__('module, fields are required for %s api', 'bit-integrations'), 'BuddyBoss')); + } + $recordApiHelper = new RecordApiHelper($integrationDetails, $integId); + $buddyBossApiResponse = $recordApiHelper->execute( + $mainAction, + $fieldValues, + $fieldMap, + $integrationDetails + ); + + if (is_wp_error($buddyBossApiResponse)) { + return $buddyBossApiResponse; + } + + return $buddyBossApiResponse; + } +} diff --git a/backend/Actions/BuddyBoss/Hooks.php b/backend/Actions/BuddyBoss/Hooks.php new file mode 100644 index 000000000..9d4dc3bf4 --- /dev/null +++ b/backend/Actions/BuddyBoss/Hooks.php @@ -0,0 +1,12 @@ +_integrationDetails = $integrationDetails; + self::$integrationID = $integId; + } + + public static function getIntegrationId() + { + return $integrationID = self::$integrationID; + } + + public function getAssignmentList() + { + return $assignment_list = $this->assignment_list; + } + + public function generateReqDataFromFieldMap($data, $fieldMap) + { + $dataFinal = []; + + foreach ($fieldMap as $key => $value) { + $triggerValue = $value->formField; + $actionValue = $value->buddyBossFormField; + if ($triggerValue === 'custom') { + $dataFinal[$actionValue] = Common::replaceFieldWithValue($value->customValue, $data); + } elseif (!\is_null($data[$triggerValue])) { + $dataFinal[$actionValue] = $data[$triggerValue]; + } + } + + return $dataFinal; + } + + public function createGroup($privacyId, $finalData) + { + switch ($privacyId) { + case '1': + $privacy = 'public'; + + break; + case '2': + $privacy = 'private'; + + break; + case '3': + $privacy = 'hidden'; + + break; + } + + $user_id = get_current_user_id(); + $data = [ + 'creator_id' => $user_id, + 'name' => $finalData['group_name'], + 'status' => $privacy + ]; + + return groups_create_group($data); + } + + public static function addUserToGroup($groupId) + { + $user_id = get_current_user_id(); + if (\function_exists('groups_join_group')) { + $response = groups_join_group($groupId, $user_id); + if ($response) { + LogHandler::save(self::$integrationID, wp_json_encode(['type' => 'group', 'type_name' => 'add-user-to-group']), 'success', wp_json_encode(__('Successfully add user to group', 'bit-integrations'))); + } else { + LogHandler::save(self::$integrationID, wp_json_encode(['type' => 'group', 'type_name' => 'add-user-to-group']), 'error', wp_json_encode(__('Unauthorized user', 'bit-integrations'))); + } + } else { + LogHandler::save(self::$integrationID, wp_json_encode(['type' => 'group', 'type_name' => 'add-user-to-group']), 'error', wp_json_encode(__('Failed to add user to group', 'bit-integrations'))); + } + } + + public static function EndFriendshipWithUser($friendId) + { + $user_id = get_current_user_id(); + if (\function_exists('friends_remove_friend')) { + friends_remove_friend($user_id, $friendId); + LogHandler::save(self::$integrationID, wp_json_encode(['type' => 'friend', 'type_name' => 'end-friendship-with-user']), 'success', wp_json_encode(__('Successfully end friendship with user', 'bit-integrations'))); + } else { + LogHandler::save(self::$integrationID, wp_json_encode(['type' => 'friend', 'type_name' => 'end-friendship-with-user']), 'error', wp_json_encode(__('Failed to end friendship with user', 'bit-integrations'))); + } + } + + public static function FollowUser($friendId) + { + $user_id = get_current_user_id(); + + $data = [ + 'follower_id' => $user_id, + 'leader_id' => $friendId, + ]; + + if ($user_id === $friendId) { + LogHandler::save(self::$integrationID, wp_json_encode(['type' => 'friend', 'type_name' => 'follow-user']), 'error', wp_json_encode(__('A user can not follow itself.', 'bit-integrations'))); + + return; + } + + if (bp_is_active('follow') && \function_exists('bp_follow_start_following')) { + $following = bp_follow_start_following($data); + } elseif (\function_exists('bp_start_following')) { + $following = bp_start_following($data); + } + if ($following) { + // translators: %s: Placeholder value + return LogHandler::save(self::$integrationID, wp_json_encode(['type' => 'friend', 'type_name' => 'follow-user']), 'success', wp_json_encode(wp_sprintf(__('The user successfully start following a member ID - %s', 'bit-integrations'), $friendId))); + } + // translators: %s: Placeholder value + LogHandler::save(self::$integrationID, wp_json_encode(['type' => 'friend', 'type_name' => 'follow-user']), 'error', wp_json_encode(wp_sprintf(__('The user was already following a member ID - %s', 'bit-integrations'), $friendId))); + } + + // for action 5 + public static function posTopicForum($forum_id, $finalData) + { + $user_id = get_current_user_id(); + $data = [ + 'forum_id' => $forum_id, + 'topic_title' => do_shortcode($finalData['topic_title']), + 'topic_content' => do_shortcode($finalData['topic_content']), + 'topic_author' => $user_id, + ]; + + if (!empty($forum_id)) { + if (bbp_is_forum_category($forum_id)) { + LogHandler::save(self::$integrationID, wp_json_encode(['type' => 'topic', 'type_name' => 'post-topic-forum']), 'error', wp_json_encode(__('Sorry, This forum is a category. No discussions can be created in this forum.', 'bit-integrations'))); + + return; + } + if (bbp_is_forum_closed($forum_id) && !current_user_can('edit_forum', $forum_id)) { + LogHandler::save(self::$integrationID, wp_json_encode(['type' => 'topic', 'type_name' => 'post-topic-forum']), 'error', wp_json_encode(__('Sorry, This forum has been closed to new discussions.', 'bit-integrations'))); + + return; + } + + $is_member = false; + $group_ids = []; + if (\function_exists('bbp_get_forum_group_ids')) { + $group_ids = bbp_get_forum_group_ids($forum_id); + if (!empty($group_ids)) { + foreach ($group_ids as $group_id) { + if (groups_is_user_member($user_id, $group_id)) { + $is_member = true; + + break; + } + } + } + } + + if (bbp_is_forum_private($forum_id) && !bbp_is_user_keymaster()) { + if ( + (empty($group_ids) && !current_user_can('read_private_forums')) + || (!empty($group_ids) && !$is_member) + ) { + LogHandler::save(self::$integrationID, wp_json_encode(['type' => 'topic', 'type_name' => 'post-topic-forum']), 'error', wp_json_encode(__('Sorry, This forum is private and you do not have the capability to read or create new discussions in it.', 'bit-integrations'))); + + return; + } + } elseif (bbp_is_forum_hidden($forum_id) && !bbp_is_user_keymaster()) { + if ( + (empty($group_ids) && !current_user_can('read_hidden_forums')) + || (!empty($group_ids) && !$is_member) + ) { + LogHandler::save(self::$integrationID, wp_json_encode(['type' => 'topic', 'type_name' => 'post-topic-forum']), 'error', wp_json_encode(__('Sorry, This forum is hidden and you do not have the capability to read or create new discussions in it.', 'bit-integrations'))); + + return; + } + } + } + + if (!bbp_check_for_duplicate( + [ + 'post_type' => bbp_get_topic_post_type(), + 'post_author' => $user_id, + 'post_content' => do_shortcode($finalData['topic_content']) + ] + )) { + LogHandler::save(self::$integrationID, wp_json_encode(['type' => 'topic', 'type_name' => 'post-topic-forum']), 'error', wp_json_encode(__('Duplicate discussion detected; it looks as though you\'ve already said that!', 'bit-integrations'))); + + return; + } + + if (!bbp_check_for_blacklist(null, $user_id, do_shortcode($finalData['topic_title']), do_shortcode($finalData['topic_content']))) { + LogHandler::save(self::$integrationID, wp_json_encode(['type' => 'topic', 'type_name' => 'post-topic-forum']), 'error', wp_json_encode(__('Sorry, Your discussion cannot be created at this time.', 'bit-integrations'))); + + return; + } + + $topic_data = apply_filters( + 'bbp_new_topic_pre_insert', + [ + 'post_author' => $user_id, + 'post_title' => $data['topic_title'], + 'post_content' => $data['topic_content'], + 'post_status' => 'publish', + 'post_parent' => $forum_id, + 'post_type' => bbp_get_topic_post_type(), + 'tax_input' => [], + 'comment_status' => 'closed', + ] + ); + + $topic_id = wp_insert_post($topic_data); + + if (empty($topic_id) || is_wp_error($topic_id)) { + LogHandler::save(self::$integrationID, wp_json_encode(['type' => 'topic', 'type_name' => 'post-topic-forum']), 'error', wp_json_encode(__('We are facing a problem to creating a topic.', 'bit-integrations'))); + + return; + } + // translators: %s: Placeholder value + LogHandler::save(self::$integrationID, wp_json_encode(['type' => 'topic', 'type_name' => 'post-topic-forum']), 'success', wp_json_encode(wp_sprintf(__('Post created successfully and id is %s', 'bit-integrations'), $topic_id))); + + if ( + (bbp_get_trash_status_id() === get_post_field('post_status', $forum_id)) + || (bbp_get_trash_status_id() === $topic_data['post_status']) + ) { + wp_trash_post($topic_id); + } + + if (bbp_get_spam_status_id() === $topic_data['post_status']) { + add_post_meta($topic_id, '_bbp_spam_meta_status', bbp_get_public_status_id()); + } + + remove_action('bbp_new_topic', 'bbp_notify_forum_subscribers', 11); + + do_action('bbp_new_topic', $topic_id, $forum_id, null, $user_id); + + if (bbp_is_subscriptions_active()) { + $author_id = bbp_get_user_id(0, true, true); + $subscribed = bbp_is_user_subscribed($author_id, $topic_id); + + if (true === $subscribed && empty($topic->bbp_topic_subscription)) { + bbp_remove_user_subscription($author_id, $topic_id); + } elseif (false === $subscribed && !empty($topic->bbp_topic_subscription)) { + bbp_add_user_subscription($author_id, $topic_id); + } + } + + do_action('bbp_new_topic_post_extras', $topic_id); + + if (\function_exists('bbp_notify_forum_subscribers')) { + bbp_notify_forum_subscribers($topic_id, $forum_id, null, $user_id); + } + } + + // action 6 start + + public static function removeUserFromGroup($group_id) + { + $user_id = get_current_user_id(); + + if ($group_id === 'any') { + $all_user_groups = groups_get_user_groups($user_id); + if (!empty($all_user_groups['groups'])) { + foreach ($all_user_groups['groups'] as $group) { + $result = groups_leave_group($group, $user_id); + } + } + } else { + $result = groups_leave_group($group_id, $user_id); + } + + return $result; + } + + public static function sendFriendshipRequestUser($friendId) + { + $user_id = get_current_user_id(); + if (\function_exists('friends_add_friend')) { + return friends_add_friend($user_id, $friendId); + } + + return false; + } + + public static function sendNotificationMembersGroup($group_id, $friendId, $finalData) + { + $user_id = get_current_user_id(); + $data = [ + 'group_id' => $group_id, + 'friend_id' => $friendId, + 'notification_content' => do_shortcode($finalData['notification_content']), + 'notification_link' => do_shortcode($finalData['notification_link']), + ]; + + if (\function_exists('groups_get_group_members')) { + $members = groups_get_group_members([ + 'group_id' => $group_id, + 'per_page' => 999999, + 'type' => 'last_joined', + 'exclude_banned' => true + ]); + + if (isset($members['members'])) { + if (\function_exists('bp_notifications_add_notification')) { + foreach ($members['members'] as $member) { + $notification_id = ''; + $notification_id = bp_notifications_add_notification( + [ + 'user_id' => $member->ID, + 'item_id' => 1, + 'secondary_item_id' => $user_id, + 'component_name' => 'bit-integrations', + 'component_action' => 'bit_integrations_send_notification', + 'date_notified' => bp_core_current_time(), + 'is_new' => 1, + 'allow_duplicate' => true, + ] + ); + bp_notifications_update_meta($notification_id, 'uo_notification_content', $data['notification_content']); + bp_notifications_update_meta($notification_id, 'uo_notification_link', $data['notification_link']); + } + + return true; + } + } + } else { + return false; + } + } + + public static function SendPrivateMessageMembersGroup($group_id, $friendId, $finalData) + { + $user_id = get_current_user_id(); + $data = [ + 'group_id' => $group_id, + 'friend_id' => $friendId, + 'message_content' => do_shortcode($finalData['message_content']), + 'message_subject' => do_shortcode($finalData['message_subject']), + ]; + + if (\function_exists('groups_get_group_members')) { + $members = groups_get_group_members(['group_id' => $group_id, 'per_page' => -1, 'type' => 'last_joined', 'exclude_banned' => true]); + + if (isset($members['members'])) { + foreach ($members['members'] as $member) { + $members_ids[] = $member->ID; + } + + $msg = [ + 'sender_id' => $friendId, + 'recipients' => $members_ids, + 'subject' => $data['message_subject'], + 'content' => $data['message_content'], + 'error_type' => 'wp_error', + ]; + + if (\function_exists('messages_new_message')) { + $send = messages_new_message($msg); + if (is_wp_error($send)) { + $messages = $send->get_error_messages(); + $err = []; + if ($messages) { + foreach ($messages as $msg) { + $err[] = $msg; + } + } + + return false; + } + + return $send; + } + } + } else { + return false; + } + } + + public static function SendPrivateMessageUser($friendId, $finalData, $fieldValues, $recipientUserId = null) + { + $user_id = (empty($recipientUserId) || $recipientUserId === 'loggedInUser') ? get_current_user_id() : Common::replaceFieldWithValue($recipientUserId, $fieldValues); + $data = [ + 'sender_id' => $friendId, + 'message_content' => do_shortcode($finalData['message_content']), + 'message_subject' => do_shortcode($finalData['message_subject']), + ]; + + $msg = [ + 'sender_id' => $data['sender_id'], + 'recipients' => [$user_id], + 'subject' => $data['message_subject'], + 'content' => $data['message_content'], + 'error_type' => 'wp_error', + ]; + + if (\function_exists('messages_new_message')) { + $send = messages_new_message($msg); + if (is_wp_error($send)) { + $messages = $send->get_error_messages(); + $err = []; + if ($messages) { + foreach ($messages as $msg) { + $err[] = $msg; + } + } + + return false; + } + + return $send; + } + + return false; + } + + public static function sendNotificationUser($finalData) + { + $user_id = get_current_user_id(); + + if (\function_exists('bp_notifications_add_notification')) { + $notification_id = bp_notifications_add_notification( + [ + 'user_id' => $user_id, + 'item_id' => 1, + 'secondary_item_id' => $user_id, + 'component_name' => 'bit-integrations', + 'component_action' => 'bit_integrations_send_notification', + 'date_notified' => bp_core_current_time(), + 'is_new' => 1, + 'allow_duplicate' => true, + ] + ); + + $data = [ + 'notification_content' => do_shortcode($finalData['notification_content']), + 'notification_link' => do_shortcode($finalData['notification_link']), + ]; + + if (is_wp_error($notification_id)) { + return false; + } + if (!empty($data['notification_link'])) { + $notification_content = '' . ($data['notification_content']) . ''; + } + + bp_notifications_update_meta($notification_id, 'uo_notification_content', $notification_content); + bp_notifications_update_meta($notification_id, 'uo_notification_link', $data['notification_link']); + + return true; + } + + return false; + } + + public static function stopFollowingUser($friendId) + { + $user_id = get_current_user_id(); + $follower_ids = explode(',', $friendId); + + if (\function_exists('bp_stop_following') || (bp_is_active('follow') && \function_exists('bp_follow_stop_following'))) { + foreach ($follower_ids as $k => $follower_id) { + if ((int) $follower_id == $user_id) { + continue; + } + $message = ''; + $data = [ + 'follower_id' => $user_id, + 'leader_id' => (int) $follower_id + ]; + if (bp_is_active('follow') && \function_exists('bp_follow_stop_following')) { + $following = bp_follow_stop_following($data); + } elseif (\function_exists('bp_stop_following')) { + $following = bp_stop_following($data); + } + if ($following == false) { + $message .= 'The user was not following a member id is - ' . $follower_id . '. '; + LogHandler::save(self::$integrationID, wp_json_encode(['type' => 'follow', 'type_name' => 'stop-follow-user']), 'error', wp_json_encode($message)); + } else { + LogHandler::save(self::$integrationID, wp_json_encode(['type' => 'follow', 'type_name' => 'stop-follow-user']), 'success', wp_json_encode(__('Stop following users successfully .', 'bit-integrations'))); + } + } + } + } + + public static function subscribeForum($forumId) + { + if (bbp_is_subscriptions_active() === false) { + return; + } + $user_id = get_current_user_id(); + $forum_ids = explode(',', $forumId); + + if (!empty($forum_ids)) { + foreach ($forum_ids as $forum_id) { + $is_subscription = bbp_is_user_subscribed($user_id, (int) $forum_id); + $success = false; + + if (true === $is_subscription) { + LogHandler::save(self::$integrationID, wp_json_encode(['type' => 'subscribed', 'type_name' => 'subscribe-forum']), 'error', wp_json_encode(__('The user is already subscribed to the specified forum.', 'bit-integrations'))); + + return; + } + $success = bbp_add_user_subscription($user_id, (int) $forum_id); + // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- Using BuddyBoss's own hook for compatibility + do_action('buddyBoss_subscriptions_handler', $success, $user_id, (int) $forum_id, 'bbp_subscribe'); + + if ($success === false && $is_subscription === false) { + LogHandler::save(self::$integrationID, wp_json_encode(['type' => 'subscribed', 'type_name' => 'subscribe-forum']), 'error', wp_json_encode(__('There was a problem subscribing to that forum!', 'bit-integrations'))); + + return; + } + + return $success; + } + } + } + + public static function addPostToGroup($group_id, $friendId, $finalData) + { + $action_author = $friendId; + $data = [ + 'action' => $finalData['activity_action'], + 'action_content' => $finalData['activity_content'], + ]; + + if (empty($group_id)) { + return false; + } + $activity = false; + + if ('any' === $group_id) { + global $wpdb; + $cache_key = Config::withPrefix('buddyboss_groups_all_statuses'); + $cache_group = Config::VAR_PREFIX; + $results = wp_cache_get($cache_key, $cache_group); + + if (false === $results) { + // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.PreparedSQL.NotPrepared,WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- Dynamic IN clause with sprintf for placeholders. + $results = $wpdb->get_results("SELECT * FROM {$wpdb->prefix}bp_groups WHERE status IN ('public', 'private', 'hidden')"); + wp_cache_set($cache_key, $results, $cache_group, 10 * MINUTE_IN_SECONDS); + } + if ($results) { + foreach ($results as $result) { + $hide_sitewide = false; + if (\in_array($result->status, ['private', 'hidden'], true)) { + $hide_sitewide = true; + } + $activity = bp_activity_add([ + 'action' => $data['action'], + 'content' => $data['action_content'], + 'primary_link' => null, + 'component' => 'groups', + 'item_id' => $result->id, + 'type' => 'activity_update', + 'user_id' => $action_author, + 'hide_sitewide' => $hide_sitewide, + ]); + if (is_wp_error($activity)) { + break; + } + if (!$activity) { + break; + } + } + } + } else { + global $wpdb; + $cache_key = Config::withPrefix('buddyboss_group_') . absint($group_id); + $cache_group = Config::VAR_PREFIX; + $results = wp_cache_get($cache_key, $cache_group); + + if (false === $results) { + // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- Direct query needed for BuddyBoss groups. + $results = $wpdb->get_results($wpdb->prepare("SELECT * FROM {$wpdb->prefix}bp_groups WHERE id = %d", $group_id)); + wp_cache_set($cache_key, $results, $cache_group, 10 * MINUTE_IN_SECONDS); + } + if ($results) { + foreach ($results as $result) { + $hide_sitewide = false; + if (\in_array($result->status, ['private', 'hidden'], true)) { + $hide_sitewide = true; + } + $activity = bp_activity_add([ + 'action' => $data['action'], + 'content' => $data['action_content'], + 'primary_link' => null, + 'component' => 'groups', + 'item_id' => $result->id, + 'type' => 'activity_update', + 'user_id' => $action_author, + 'hide_sitewide' => $hide_sitewide, + ]); + if (is_wp_error($activity)) { + break; + } + if (!$activity) { + break; + } + } + } + } + if (is_wp_error($activity)) { + $error_message = $activity->get_error_message(); + + return $error_message; + } elseif (!$activity) { + return $error_message = __('There is an error on posting stream.', 'bit-integrations'); + } + + return $activity; + } + + public static function postActivityStream($friendId, $finalData) + { + $data = [ + 'action' => $finalData['activity_action'], + 'action_link' => $finalData['activity_link'], + 'action_content' => $finalData['activity_content'], + ]; + + return bp_activity_add([ + 'action' => $data['action'], + 'content' => $data['action_content'], + 'primary_link' => $data['action_link'], + 'component' => 'activity', + 'type' => 'activity_update', + 'user_id' => (int) $friendId, + 'hide_sitewide' => false, + ]); + } + + public static function postActivityUsersStream($friendId, $finalData) + { + $data = [ + 'action' => $finalData['activity_action'], + 'action_link' => $finalData['activity_link'], + 'action_content' => $finalData['activity_content'], + ]; + + return bp_activity_add([ + 'action' => $data['action'], + 'content' => $data['action_content'], + 'primary_link' => $data['action_link'], + 'component' => 'activity', + 'type' => 'activity_update', + 'user_id' => (int) $friendId, + 'hide_sitewide' => true, + ]); + } + + public static function postReplyTopicForum($forum_id, $topic_id, $finalData) + { + $data = [ + 'forum_id' => $forum_id, + 'topic_id' => $topic_id, + 'reply_content' => $finalData['reply_content'], + 'reply_author' => get_current_user_id(), + 'anonymous_data' => 0, + 'reply_title' => '', + 'reply_to' => 0, + ]; + + if (!bbp_get_topic($topic_id)) { + LogHandler::save(self::$integrationID, wp_json_encode(['type' => 'reply', 'type_name' => 'reply-forum-topic']), 'error', wp_json_encode(__('Sorry, Discussion does not exist.', 'bit-integrations'))); + + return; + } + + if (!bbp_get_forum($forum_id)) { + LogHandler::save(self::$integrationID, wp_json_encode(['type' => 'reply', 'type_name' => 'reply-forum-topic']), 'error', wp_json_encode(__('Sorry, Forum does not exist.', 'bit-integrations'))); + + return; + } + if (!empty($forum_id)) { + if (bbp_is_forum_category($forum_id)) { + LogHandler::save(self::$integrationID, wp_json_encode(['type' => 'reply', 'type_name' => 'reply-forum-topic']), 'error', wp_json_encode(__('Sorry, This forum is a category. No discussions can be created in this forum.', 'bit-integrations'))); + + return; + } + if (bbp_is_forum_closed($forum_id) && !current_user_can('edit_forum', $forum_id)) { + LogHandler::save(self::$integrationID, wp_json_encode(['type' => 'reply', 'type_name' => 'reply-forum-topic']), 'error', wp_json_encode(__('Sorry, This forum has been closed to new discussions.', 'bit-integrations'))); + + return; + } + + $is_member = false; + $group_ids = []; + if (\function_exists('bbp_get_forum_group_ids')) { + $group_ids = bbp_get_forum_group_ids($forum_id); + if (!empty($group_ids)) { + foreach ($group_ids as $group_id) { + if (groups_is_user_member($reply_author, $group_id)) { + $is_member = true; + + break; + } + } + } + } + + if (bbp_is_forum_private($forum_id) && !bbp_is_user_keymaster()) { + if ( + (empty($group_ids) && !current_user_can('read_private_forums')) + || (!empty($group_ids) && !$is_member) + ) { + LogHandler::save(self::$integrationID, wp_json_encode(['type' => 'reply', 'type_name' => 'reply-forum-topic']), 'error', wp_json_encode(__('Sorry, This forum is private and you do not have the capability to read or create new discussions in it.', 'bit-integrations'))); + + return; + } + } elseif (bbp_is_forum_hidden($forum_id) && !bbp_is_user_keymaster()) { + if ( + (empty($group_ids) && !current_user_can('read_hidden_forums')) + || (!empty($group_ids) && !$is_member) + ) { + LogHandler::save(self::$integrationID, wp_json_encode(['type' => 'reply', 'type_name' => 'reply-forum-topic']), 'error', wp_json_encode(__('Sorry, This forum is hidden and you do not have the capability to read or create new discussions in it.', 'bit-integrations'))); + + return; + } + } + } + + $reply_content = apply_filters('bbp_new_reply_pre_content', $data['reply_content']); + + if (!bbp_check_for_duplicate( + [ + 'post_type' => bbp_get_reply_post_type(), + 'post_author' => $data['reply_author'], + 'post_content' => $reply_content, + 'post_parent' => $topic_id, + 'anonymous_data' => $data['anonymous_data'], + ] + )) { + LogHandler::save(self::$integrationID, wp_json_encode(['type' => 'reply', 'type_name' => 'reply-forum-topic']), 'error', wp_json_encode("Duplicate reply detected; it looks as though you've already said that!")); + + return; + } + + if (bbp_is_topic_closed($topic_id) && !current_user_can('moderate')) { + LogHandler::save(self::$integrationID, wp_json_encode(['type' => 'reply', 'type_name' => 'reply-forum-topic']), 'error', wp_json_encode(__('Sorry, Discussion is closed.', 'bit-integrations'))); + + return; + } + + if (!bbp_check_for_blacklist($data['anonymous_data'], $data['reply_author'], $data['reply_title'], $reply_content)) { + LogHandler::save(self::$integrationID, wp_json_encode(['type' => 'reply', 'type_name' => 'reply-forum-topic']), 'error', wp_json_encode(__('Sorry, Your reply cannot be created at this time.', 'bit-integrations'))); + + return; + } + + if (!bbp_check_for_moderation($data['anonymous_data'], $data['reply_author'], $data['reply_title'], $reply_content)) { + $reply_status = bbp_get_pending_status_id(); + } else { + $reply_status = bbp_get_public_status_id(); + } + + if (bbp_is_topic_closed($topic_id) && !current_user_can('moderate')) { + LogHandler::save(self::$integrationID, wp_json_encode(['type' => 'reply', 'type_name' => 'reply-forum-topic']), 'error', wp_json_encode(__('Sorry, Discussion is closed.', 'bit-integrations'))); + + return; + } + $reply_data = apply_filters( + 'bbp_new_reply_pre_insert', + [ + 'post_author' => $data['reply_author'], + 'post_title' => $data['reply_title'], + 'post_content' => $data['reply_content'], + 'post_status' => $reply_status, + 'post_parent' => $topic_id, + 'post_type' => bbp_get_reply_post_type(), + 'comment_status' => 'closed', + 'menu_order' => bbp_get_topic_reply_count($topic_id, false) + 1, + ] + ); + + $reply_id = wp_insert_post($reply_data); + + if (empty($reply_id) || is_wp_error($reply_id)) { + $append_error = ( + (is_wp_error($reply_id) && $reply_id->get_error_message()) + ? __('The following problems have been found with your reply:', 'bit-integrations') . ' ' . $reply_id->get_error_message() + : __('We are facing a problem to creating a reply.', 'bit-integrations') + ); + + LogHandler::save(self::$integrationID, wp_json_encode(['type' => 'reply', 'type_name' => 'reply-forum-topic']), 'error', wp_json_encode($append_error)); + + return; + } + + if (bbp_is_topic_trash($topic_id) || (bbp_get_trash_status_id() === $reply_data['post_status'])) { + wp_trash_post($reply_id); + + if (bbp_is_topic_trash($topic_id)) { + $pre_trashed_replies = (array) get_post_meta($topic_id, '_bbp_pre_trashed_replies', true); + + $pre_trashed_replies[] = $reply_id; + + update_post_meta($topic_id, '_bbp_pre_trashed_replies', $pre_trashed_replies); + } + } elseif (bbp_is_topic_spam($topic_id) || (bbp_get_spam_status_id() === $reply_data['post_status'])) { + add_post_meta($reply_id, '_bbp_spam_meta_status', bbp_get_public_status_id()); + + if (bbp_is_topic_spam($topic_id)) { + $pre_spammed_replies = (array) get_post_meta($topic_id, '_bbp_pre_spammed_replies', true); + + $pre_spammed_replies[] = $reply_id; + + update_post_meta($topic_id, '_bbp_pre_spammed_replies', $pre_spammed_replies); + } + } + + remove_action('bbp_new_reply', 'bbp_notify_topic_subscribers', 11); + + do_action('bbp_new_reply', $reply_id, $topic_id, $forum_id, $data['anonymous_data'], $data['reply_author'], false, $data['reply_to']); + + do_action('bbp_new_reply_post_extras', $reply_id); + + return $reply_id; + } + + public static function setUserStatus($userStatusId) + { + $user_id = get_current_user_id(); + $set_user_status = $userStatusId === '1' ? 'active' : 'suspend'; + if (bp_is_active('moderation')) { + if ('suspend' === $set_user_status) { + BP_Suspend_Member::suspend_user($user_id); + } elseif (bp_moderation_is_user_suspended($user_id)) { + BP_Suspend_Member::unsuspend_user($user_id); + } + + return true; + } + + return false; + } + + public function execute( + $mainAction, + $fieldValues, + $fieldMap, + $integrationDetails + ) { + $fieldData = []; + $apiResponse = null; + if ($mainAction == static::CREATE_GROUP_PRO) { + $privacyId = $integrationDetails->privacyId; + $finalData = $this->generateReqDataFromFieldMap($fieldValues, $fieldMap); + $apiResponse = self::createGroup( + $privacyId, + $finalData + ); + if ($apiResponse !== 0) { + // translators: %s: Placeholder value + LogHandler::save(self::$integrationID, wp_json_encode(['type' => 'group', 'type_name' => 'create-group']), 'success', wp_json_encode(wp_sprintf(__('Group created successfully and is is %s', 'bit-integrations'), $apiResponse))); + } else { + LogHandler::save(self::$integrationID, wp_json_encode(['type' => 'group', 'type_name' => 'create-group']), 'error', wp_json_encode($apiResponse)); + } + } + if ($mainAction == static::ADD_USER_GROUP) { + $groupId = $integrationDetails->groupId; + self::addUserToGroup( + $groupId + ); + } + if ($mainAction == static::END_FRIENDSHIP_WITH_USER_PRO) { + $friendId = $integrationDetails->friendId; + self::EndFriendshipWithUser( + $friendId + ); + } + if ($mainAction == static::FOLLOW_USER_PRO) { + $friendId = $integrationDetails->friendId; + self::FollowUser( + $friendId + ); + } + if ($mainAction == static::POST_TOPIC_FORUM_PRO) { + $forumId = $integrationDetails->forumId; + $finalData = $this->generateReqDataFromFieldMap($fieldValues, $fieldMap); + self::posTopicForum( + $forumId, + $finalData + ); + } + if ($mainAction == static::REMOVE_USER_FROM_GROUP_PRO) { + $groupId = $integrationDetails->groupId; + $apiResponse = self::removeUserFromGroup( + $groupId + ); + if ($apiResponse) { + LogHandler::save(self::$integrationID, wp_json_encode(['type' => 'user', 'type_name' => 'user-remove-group']), 'success', wp_json_encode(__('User removed from group successfully .', 'bit-integrations'))); + } else { + LogHandler::save(self::$integrationID, wp_json_encode(['type' => 'user', 'type_name' => 'user-remove-group']), 'error', wp_json_encode(__('Failed to remove user form group .', 'bit-integrations'))); + } + } + if ($mainAction == static::SEND_FRIENDSHIP_REQ_USER_PRO) { + $friendId = $integrationDetails->friendId; + $apiResponse = self::sendFriendshipRequestUser( + $friendId + ); + if ($apiResponse) { + LogHandler::save(self::$integrationID, wp_json_encode(['type' => 'user', 'type_name' => 'send-friend-request']), 'success', wp_json_encode(__('Send friend request successfully .', 'bit-integrations'))); + } else { + LogHandler::save(self::$integrationID, wp_json_encode(['type' => 'user', 'type_name' => 'send-friend-request']), 'error', wp_json_encode(__('Failed to send friend request to user .', 'bit-integrations'))); + } + } + if ($mainAction == static::SEND_NOTIFICATION_MEMBER_GRP_PRO) { + $group_id = $integrationDetails->groupId; + $friendId = $integrationDetails->friendId; + $finalData = $this->generateReqDataFromFieldMap($fieldValues, $fieldMap); + $apiResponse = self::sendNotificationMembersGroup($group_id, $friendId, $finalData); + if ($apiResponse) { + LogHandler::save(self::$integrationID, wp_json_encode(['type' => 'notification', 'type_name' => 'send-notification-allMember']), 'success', wp_json_encode(__('Notification are send successfully .', 'bit-integrations'))); + } else { + LogHandler::save(self::$integrationID, wp_json_encode(['type' => 'notification', 'type_name' => 'send-notification-allMember']), 'error', wp_json_encode(__('BuddyBoss notification module is not active.', 'bit-integrations'))); + } + } + if ($mainAction == static::SEND_PRIVATE_MSG_MEMBER_GRP_PRO) { + $group_id = $integrationDetails->groupId; + $friendId = $integrationDetails->friendId; + $finalData = $this->generateReqDataFromFieldMap($fieldValues, $fieldMap); + $apiResponse = self::SendPrivateMessageMembersGroup($group_id, $friendId, $finalData); + if ($apiResponse) { + LogHandler::save(self::$integrationID, wp_json_encode(['type' => 'message', 'type_name' => 'send-private-message']), 'success', wp_json_encode(__('Send private message to all group member successfully .', 'bit-integrations'))); + } else { + LogHandler::save(self::$integrationID, wp_json_encode(['type' => 'message', 'type_name' => 'send-private-message']), 'error', wp_json_encode(__('BuddyBoss message module is not active.', 'bit-integrations'))); + } + } + if ($mainAction == static::SEND_PRIVATE_MSG_USER_PRO) { + $friendId = $integrationDetails->friendId; + $recipientUserId = $integrationDetails->recipientUserId ?? null; + + $finalData = $this->generateReqDataFromFieldMap($fieldValues, $fieldMap); + $apiResponse = self::SendPrivateMessageUser($friendId, $finalData, $fieldValues, $recipientUserId); + + if ($apiResponse) { + LogHandler::save(self::$integrationID, wp_json_encode(['type' => 'message', 'type_name' => 'send-private-message']), 'success', wp_json_encode(__('Send private message to user successfully .', 'bit-integrations'))); + } else { + LogHandler::save(self::$integrationID, wp_json_encode(['type' => 'message', 'type_name' => 'send-private-message']), 'error', wp_json_encode(__('BuddyBoss message module is not active.', 'bit-integrations'))); + } + } + if ($mainAction == static::SEND_NOTIFICATION_USER_PRO) { + $finalData = $this->generateReqDataFromFieldMap($fieldValues, $fieldMap); + $apiResponse = self::sendNotificationUser($finalData); + if ($apiResponse) { + LogHandler::save(self::$integrationID, wp_json_encode(['type' => 'notification', 'type_name' => 'send-notification-allMember']), 'success', wp_json_encode(__('Notification are send successfully .', 'bit-integrations'))); + } else { + LogHandler::save(self::$integrationID, wp_json_encode(['type' => 'notification', 'type_name' => 'send-notification-allMember']), 'error', wp_json_encode(__('BuddyBoss message module is not active.', 'bit-integrations'))); + } + } + if ($mainAction == static::STOP_FOLLOWING_USER_PRO) { + $friendId = $integrationDetails->friendId; + $apiResponse = self::stopFollowingUser( + $friendId + ); + } + if ($mainAction == static::SUBSCRIBE_USER_FORUM_PRO) { + $forum_id = $integrationDetails->forumId; + $apiResponse = self::subscribeForum( + $forum_id + ); + if ($apiResponse) { + LogHandler::save(self::$integrationID, wp_json_encode(['type' => 'subscribe', 'type_name' => 'subscribe-forum']), 'success', wp_json_encode(__('Forum subscribe successfully .', 'bit-integrations'))); + } else { + LogHandler::save(self::$integrationID, wp_json_encode(['type' => 'subscribe', 'type_name' => 'subscribe-forum']), 'error', wp_json_encode(__('Failed to subscribe Forum .', 'bit-integrations'))); + } + } + if ($mainAction == static::ADD_POST_GRP_ACTIVITY_STREAM_PRO) { + $group_id = $integrationDetails->groupId; + $friendId = $integrationDetails->friendId; + $finalData = $this->generateReqDataFromFieldMap($fieldValues, $fieldMap); + $apiResponse = self::addPostToGroup($group_id, $friendId, $finalData); + if (\gettype($apiResponse) === 'integer') { + // translators: %s: Placeholder value + LogHandler::save(self::$integrationID, wp_json_encode(['type' => 'post', 'type_name' => 'add-post-to-group']), 'success', wp_json_encode(wp_sprintf(__('Post added to group successfully and id is -> %s', 'bit-integrations'), $apiResponse))); + } else { + LogHandler::save(self::$integrationID, wp_json_encode(['type' => 'post', 'type_name' => 'add-post-to-group']), 'error', wp_json_encode($apiResponse)); + } + } + if ($mainAction == static::ADD_POST_SITE_WIDE_ACTIVITY_STREAM_PRO) { + $friendId = $integrationDetails->friendId; + $finalData = $this->generateReqDataFromFieldMap($fieldValues, $fieldMap); + $apiResponse = self::postActivityStream($friendId, $finalData); + if (\gettype($apiResponse) === 'integer') { + // translators: %s: Placeholder value + LogHandler::save(self::$integrationID, wp_json_encode(['type' => 'post', 'type_name' => 'add-post-sitewide-activity']), 'success', wp_json_encode(wp_sprintf(__('Post added to sitewide activity stream successfully and id is -> %s', 'bit-integrations'), $apiResponse))); + } else { + LogHandler::save(self::$integrationID, wp_json_encode(['type' => 'post', 'type_name' => 'add-post-sitewide-activity']), 'error', wp_json_encode($apiResponse)); + } + } + if ($mainAction == static::ADD_POST_USER_ACTIVITY_STREAM_PRO) { + $friendId = $integrationDetails->friendId; + $finalData = $this->generateReqDataFromFieldMap($fieldValues, $fieldMap); + $apiResponse = self::postActivityUsersStream($friendId, $finalData); + if (\gettype($apiResponse) === 'integer') { + // translators: %s: Placeholder value + LogHandler::save(self::$integrationID, wp_json_encode(['type' => 'post', 'type_name' => 'add-post-user-activity']), 'success', wp_json_encode(wp_sprintf(__('Post added to Users activity stream successfully and id is -> %s', 'bit-integrations'), $apiResponse))); + } else { + LogHandler::save(self::$integrationID, wp_json_encode(['type' => 'post', 'type_name' => 'add-post-user-activity']), 'error', wp_json_encode($apiResponse)); + } + } + if ($mainAction == static::POST_REPLY_TOPIC_FORUM_PRO) { + $forum_id = $integrationDetails->forumId; + $topic_id = $integrationDetails->topicId; + $finalData = $this->generateReqDataFromFieldMap($fieldValues, $fieldMap); + $apiResponse = self::postReplyTopicForum($forum_id, $topic_id, $finalData); + if (\gettype($apiResponse) === 'integer') { + // translators: %s: Placeholder value + LogHandler::save(self::$integrationID, wp_json_encode(['type' => 'reply', 'type_name' => 'reply-forum-topic']), 'success', wp_json_encode(wp_sprintf(__('Reply forum topic successfully and id is -> %s', 'bit-integrations'), $apiResponse))); + } else { + LogHandler::save(self::$integrationID, wp_json_encode(['type' => 'reply', 'type_name' => 'reply-forum-topic']), 'error', wp_json_encode(__('Failed to reply forum topic.', 'bit-integrations'))); + } + } + if ($mainAction == static::SET_USER_STATUS_PRO) { + $userStatusId = $integrationDetails->userStatusId; + $apiResponse = self::setUserStatus($userStatusId); + if ($apiResponse) { + LogHandler::save(self::$integrationID, wp_json_encode(['type' => 'user', 'type_name' => 'user-specific-status']), 'success', wp_json_encode(__('Change user status successfully .', 'bit-integrations'))); + } else { + LogHandler::save(self::$integrationID, wp_json_encode(['type' => 'user', 'type_name' => 'user-specific-status']), 'error', wp_json_encode(__('To change members status in your network, please activate the Moderation component.', 'bit-integrations'))); + } + } + + return $apiResponse; + } +} diff --git a/backend/Actions/BuddyBoss/Routes.php b/backend/Actions/BuddyBoss/Routes.php new file mode 100644 index 000000000..25f1b9535 --- /dev/null +++ b/backend/Actions/BuddyBoss/Routes.php @@ -0,0 +1,14 @@ +Code)) { wp_send_json_success($fields, 200); } else { + // translators: %s: Placeholder value wp_send_json_error(wp_sprintf(__('Field fetching failed: %s', 'bit-integrations'), $apiResponse->Message), 400); } } @@ -84,6 +85,7 @@ public function execute($integrationData, $fieldValues) $apiKey = $integrationDetails->api_key; if (empty($fieldMap) || empty($apiKey) || empty($selectedList)) { + // translators: %s: Placeholder value return new WP_Error('REQ_FIELD_EMPTY', wp_sprintf(__('module, fields are required for %s api', 'bit-integrations'), 'Campaign Monitor')); } diff --git a/backend/Actions/CampaignMonitor/RecordApiHelper.php b/backend/Actions/CampaignMonitor/RecordApiHelper.php new file mode 100644 index 000000000..90bfe8910 --- /dev/null +++ b/backend/Actions/CampaignMonitor/RecordApiHelper.php @@ -0,0 +1,117 @@ +_integrationDetails = $integrationDetails; + $this->_integrationID = $integId; + $this->baseUrl = 'https://api.createsend.com/api/v3.3'; + $this->_defaultHeader = [ + 'Authorization' => 'Basic ' . base64_encode("{$apiKey}:"), + 'Content-Type' => 'application/json' + ]; + } + + public function updateSubscriber($email, $finalData, $selectedList) + { + $apiEndpoints = $this->baseUrl . "/subscribers/{$selectedList}.json?email={$email}"; + + return HttpHelper::request($apiEndpoints, 'PUT', $this->setSubscriberData($finalData), $this->_defaultHeader); + } + + public function addSubscriber($selectedList, $finalData) + { + $apiEndpoints = $this->baseUrl . "/subscribers/{$selectedList}.json"; + + return HttpHelper::post($apiEndpoints, $this->setSubscriberData($finalData), $this->_defaultHeader); + } + + public function generateReqDataFromFieldMap($data, $fieldMap) + { + $dataFinal = []; + foreach ($fieldMap as $value) { + $triggerValue = $value->formField; + $actionValue = $value->campaignMonitorField; + $dataFinal[$actionValue] = ($triggerValue === 'custom') ? Common::replaceFieldWithValue($value->customValue, $data) : $data[$triggerValue]; + } + + return $dataFinal; + } + + public function execute($selectedList, $fieldValues, $fieldMap, $actions) + { + $finalData = (object) $this->generateReqDataFromFieldMap($fieldValues, $fieldMap); + $existSubscriber = $this->existSubscriber($selectedList, $finalData->EmailAddress); + + if (!isset($existSubscriber->EmailAddress)) { + $apiResponse = $this->addSubscriber($selectedList, $finalData); + + if (filter_var($apiResponse, FILTER_VALIDATE_EMAIL)) { + $res = ['message' => __('Subscriber added successfully', 'bit-integrations')]; + LogHandler::save($this->_integrationID, wp_json_encode(['type' => 'Subscriber', 'type_name' => 'Subscriber added']), 'success', wp_json_encode($res)); + } else { + LogHandler::save($this->_integrationID, wp_json_encode(['type' => 'Subscriber', 'type_name' => 'Adding Subscriber']), 'error', wp_json_encode($apiResponse)); + } + } else { + if ($actions->update) { + $apiResponse = $this->updateSubscriber($existSubscriber->EmailAddress, $finalData, $selectedList); + + if (empty($apiResponse)) { + $res = ['message' => 'Subscriber updated successfully']; + LogHandler::save($this->_integrationID, wp_json_encode(['type' => 'Subscriber', 'type_name' => 'Subscriber updated']), 'success', wp_json_encode($res)); + } else { + LogHandler::save($this->_integrationID, wp_json_encode(['type' => 'Subscriber', 'type_name' => 'updating Subscriber']), 'error', wp_json_encode($apiResponse)); + } + } else { + LogHandler::save($this->_integrationID, ['type' => 'Subscriber', 'type_name' => 'Adding Subscriber'], 'error', __('Email address already exists in the system', 'bit-integrations')); + } + } + + return $apiResponse; + } + + private function setSubscriberData($finalData) + { + $customParams = []; + $requestPerams = ['ConsentToTrack' => 'Yes']; + + foreach ($finalData as $key => $value) { + if (stripos($key, '[') > -1) { + $customParams[] + = (object) [ + 'Key' => $key, + 'Value' => $value + ] + ; + } else { + $requestPerams[$key] = $value; + } + } + + $requestPerams['CustomFields'] = $customParams; + + return wp_json_encode($requestPerams); + } + + private function existSubscriber($selectedList, $email) + { + $apiEndpoints = $this->baseUrl . "/subscribers/{$selectedList}.json?email={$email}&includetrackingpreference=true"; + + return HttpHelper::get($apiEndpoints, null, $this->_defaultHeader); + } +} diff --git a/backend/Actions/CampaignMonitor/Routes.php b/backend/Actions/CampaignMonitor/Routes.php new file mode 100644 index 000000000..4e1d3c6ae --- /dev/null +++ b/backend/Actions/CampaignMonitor/Routes.php @@ -0,0 +1,12 @@ +actionName; if (empty($fieldMap) || empty($authToken) || empty($actionName)) { + // translators: %s: Placeholder value return new WP_Error('REQ_FIELD_EMPTY', wp_sprintf(__('module, fields are required for %s api', 'bit-integrations'), 'Capsule CRM')); } diff --git a/backend/Actions/CapsuleCRM/RecordApiHelper.php b/backend/Actions/CapsuleCRM/RecordApiHelper.php new file mode 100644 index 000000000..79550a944 --- /dev/null +++ b/backend/Actions/CapsuleCRM/RecordApiHelper.php @@ -0,0 +1,300 @@ +integrationDetails = $integrationDetails; + $this->integrationId = $integId; + $this->apiUrl = 'https://api.capsulecrm.com/api/v2'; + $this->defaultHeader = [ + 'Authorization' => 'Bearer ' . $integrationDetails->api_key, + 'Content-Type' => 'application/json' + ]; + } + + public function addOrganisation($finalData) + { + if (empty($finalData['name'])) { + return ['success' => false, 'message' => __('Required field Name is empty', 'bit-integrations'), 'code' => 400]; + } + + $staticFieldsKeys = ['name', 'emailAddresses', 'about', 'street', 'city', 'state', 'zip', 'country', 'websites', 'phoneNumbers']; + + foreach ($finalData as $key => $value) { + if (\in_array($key, $staticFieldsKeys)) { + if (($key == 'street' || $key == 'city' || $key == 'state' || $key == 'zip' || $key == 'country')) { + $requestParams['addresses'][] = (object) [ + $key => $value + ]; + } elseif (($key == 'websites')) { + $requestParams['websites'][] = (object) [ + 'url' => $value + ]; + } elseif ($key == 'emailAddresses') { + $requestParams[$key][] = (object) [ + 'address' => $value + ]; + } elseif ($key == 'phoneNumbers') { + $requestParams[$key][] = (object) [ + 'number' => $value + ]; + } else { + $requestParams[$key] = $value; + } + } else { + $requestParams['fields'][] = (object) [ + 'value' => $value, + 'definition' => (object) ['id' => $key] + ]; + } + } + + if ($this->integrationDetails->actions->owner) { + $requestParams['owner'][] = [ + 'id' => (int) ($this->integrationDetails->selectedOwner), + ]; + } + if ($this->integrationDetails->actions->team) { + $requestParams['team'] = (int) ($this->integrationDetails->selectedTeam); + } + + $requestParams['type'] = 'organisation'; + + $this->type = 'Organisation'; + $this->typeName = 'Organisation created'; + + $apiEndpoint = $this->apiUrl . '/parties'; + + return HttpHelper::post($apiEndpoint, wp_json_encode(['party' => $requestParams]), $this->defaultHeader); + } + + public function addPerson($finalData) + { + if (empty($finalData['firstName'])) { + return ['success' => false, 'message' => 'Required field Name is empty', 'code' => 400]; + } + + $staticFieldsKeys = ['firstName', 'lastName', 'title', 'jobTitle', 'emailAddresses', 'about', 'street', 'city', 'state', 'zip', 'country', 'websites', 'phoneNumbers']; + + foreach ($finalData as $key => $value) { + if (\in_array($key, $staticFieldsKeys)) { + if (($key == 'street' || $key == 'city' || $key == 'state' || $key == 'zip' || $key == 'country')) { + $requestParams['addresses'][] = (object) [ + $key => $value + ]; + } elseif (($key == 'websites')) { + $requestParams['websites'][] = (object) [ + 'url' => $value + ]; + } elseif ($key == 'emailAddresses') { + $requestParams[$key][] = (object) [ + 'address' => $value + ]; + } elseif ($key == 'phoneNumbers') { + $requestParams[$key][] = (object) [ + 'number' => $value + ]; + } else { + $requestParams[$key] = $value; + } + } else { + $requestParams['fields'][] = (object) [ + 'value' => $value, + 'definition' => (object) ['id' => $key] + ]; + } + } + + $requestParams['type'] = 'person'; + + if ($this->integrationDetails->actions->organisation) { + $requestParams['organisation'] = (int) ($this->integrationDetails->selectedOrganisation); + } + if ($this->integrationDetails->actions->owner) { + $requestParams['owner'] = [ + 'id' => (int) ($this->integrationDetails->selectedOwner), + ]; + } + if ($this->integrationDetails->actions->team) { + $requestParams['team'] = (int) ($this->integrationDetails->selectedTeam); + } + + $this->type = 'Person'; + $this->typeName = 'Person created'; + + $apiEndpoint = $this->apiUrl . '/parties'; + + return HttpHelper::post($apiEndpoint, wp_json_encode(['party' => $requestParams]), $this->defaultHeader); + } + + public function addOpportunity($finalData) + { + if (empty($finalData['name'])) { + return ['success' => false, 'message' => 'Required field opportunity name is empty', 'code' => 400]; + } + $staticFieldsKeys = ['name', 'description', 'value', 'expectedCloseOn', 'closedOn', 'value']; + + foreach ($finalData as $key => $value) { + if (\in_array($key, $staticFieldsKeys)) { + if ($key == 'value') { + $requestParams['value'] = (object) [ + 'amount' => (int) $value + ]; + } else { + $requestParams[$key] = $value; + } + } else { + $requestParams['fields'][] = (object) [ + 'value' => $value, + 'definition' => (object) ['id' => $key] + ]; + } + } + + if (!empty($this->integrationDetails->selectedCRMParty)) { + $requestParams['party'] = [ + 'id' => (int) ($this->integrationDetails->selectedCRMParty), + ]; + } + if (!empty($this->integrationDetails->selectedCRMMilestones)) { + $requestParams['milestone'] = [ + 'id' => (int) ($this->integrationDetails->selectedCRMMilestones), + ]; + } + if ($this->integrationDetails->actions->owner) { + $requestParams['owner'] = [ + 'id' => (int) ($this->integrationDetails->selectedOwner), + ]; + } + if ($this->integrationDetails->actions->team) { + $requestParams['team'] = (int) ($this->integrationDetails->selectedTeam); + } + if (!empty($this->integrationDetails->actions->currency)) { + $requestParams['value']->currency = ($this->integrationDetails->selectedCurrency); + } + + $this->type = 'Opportunity'; + $this->typeName = 'Opportunity created'; + + $apiEndpoint = $this->apiUrl . '/opportunities'; + + return HttpHelper::post($apiEndpoint, wp_json_encode(['opportunity' => $requestParams]), $this->defaultHeader); + } + + public function addProject($finalData) + { + if (empty($finalData['name'])) { + return ['success' => false, 'message' => __('Required field project name is empty', 'bit-integrations'), 'code' => 400]; + } + $staticFieldsKeys = ['name', 'description', 'expectedCloseOn']; + + foreach ($finalData as $key => $value) { + if (\in_array($key, $staticFieldsKeys)) { + $requestParams[$key] = $value; + } else { + $requestParams['fields'][] = (object) [ + 'value' => $value, + 'definition' => (object) ['id' => $key] + ]; + } + } + + if (!empty($this->integrationDetails->selectedCRMParty)) { + $requestParams['party'] = [ + 'id' => (int) ($this->integrationDetails->selectedCRMParty), + ]; + } + if ($this->integrationDetails->actions->owner) { + $requestParams['owner'] = [ + 'id' => (int) ($this->integrationDetails->selectedOwner), + ]; + } + if ($this->integrationDetails->actions->opportunity) { + $requestParams['opportunity'] = [ + 'id' => (int) ($this->integrationDetails->selectedOpportunity), + ]; + } + if ($this->integrationDetails->actions->team) { + $requestParams['team'] = (int) ($this->integrationDetails->selectedTeam); + } + + $this->type = 'Project'; + $this->typeName = 'Project created'; + + $apiEndpoint = $this->apiUrl . '/kases'; + + return HttpHelper::post($apiEndpoint, wp_json_encode(['kase' => $requestParams]), $this->defaultHeader); + } + + public function generateReqDataFromFieldMap($data, $fieldMap) + { + $dataFinal = []; + foreach ($fieldMap as $value) { + $triggerValue = $value->formField; + $actionValue = $value->capsulecrmFormField; + if ($triggerValue === 'custom') { + if ($actionValue === 'fields') { + $dataFinal[$value->customFieldKey] = $value->customValue; + } else { + $dataFinal[$actionValue] = $value->customValue; + } + } elseif (!\is_null($data[$triggerValue])) { + if ($actionValue === 'fields') { + $dataFinal[$value->customFieldKey] = $data[$triggerValue]; + } else { + $dataFinal[$actionValue] = $data[$triggerValue]; + } + } + } + + return $dataFinal; + } + + public function execute($fieldValues, $fieldMap, $actionName) + { + $finalData = $this->generateReqDataFromFieldMap($fieldValues, $fieldMap); + if ($actionName === 'organisation') { + $apiResponse = $this->addOrganisation($finalData); + } elseif ($actionName === 'person') { + $apiResponse = $this->addPerson($finalData); + } elseif ($actionName === 'opportunity') { + $apiResponse = $this->addOpportunity($finalData); + } elseif ($actionName === 'project') { + $apiResponse = $this->addProject($finalData); + } + + if (isset($apiResponse->kase->id) || isset($apiResponse->opportunity->id) || isset($apiResponse->party->id) || isset($apiResponse->data->id) || $apiResponse->status === 'success') { + $res = [$this->typeName . ' successfully']; + LogHandler::save($this->integrationId, wp_json_encode(['type' => $this->type, 'type_name' => $this->typeName]), 'success', wp_json_encode($res)); + } else { + LogHandler::save($this->integrationId, wp_json_encode(['type' => $this->type, 'type_name' => $this->type . ' creating']), 'error', wp_json_encode($apiResponse)); + } + + return $apiResponse; + } +} diff --git a/backend/Actions/CapsuleCRM/Routes.php b/backend/Actions/CapsuleCRM/Routes.php new file mode 100644 index 000000000..e6ebd62d1 --- /dev/null +++ b/backend/Actions/CapsuleCRM/Routes.php @@ -0,0 +1,17 @@ +actionName; if (empty($fieldMap) || empty($authToken) || empty($actionName)) { + // translators: %s: Placeholder value return new WP_Error('REQ_FIELD_EMPTY', wp_sprintf(__('module, fields are required for %s api', 'bit-integrations'), 'Clickup')); } diff --git a/backend/Actions/Clickup/RecordApiHelper.php b/backend/Actions/Clickup/RecordApiHelper.php new file mode 100644 index 000000000..413eb500e --- /dev/null +++ b/backend/Actions/Clickup/RecordApiHelper.php @@ -0,0 +1,148 @@ +integrationDetails = $integrationDetails; + $this->integrationId = $integId; + $this->apiUrl = 'https://api.clickup.com/api/v2/'; + $this->defaultHeader = [ + 'Authorization' => $integrationDetails->api_key, + 'content-type' => 'application/json' + ]; + } + + public function addTask($finalData, $fieldValues) + { + if (!isset($finalData['name'])) { + return ['success' => false, 'message' => __('Required field task name is empty', 'bit-integrations'), 'code' => 400]; + } + $staticFieldsKeys = ['name', 'description', 'start_date', 'due_date']; + + foreach ($finalData as $key => $value) { + if (\in_array($key, $staticFieldsKeys)) { + if ($key === 'start_date' || $key === 'due_date') { + $requestParams[$key] = strtotime($value) * 1000; + } else { + $requestParams[$key] = $value; + } + } else { + $requestParams['custom_fields'][] = (object) [ + 'id' => $key, + 'value' => $value, + ]; + } + } + + $this->type = 'Task'; + $this->typeName = 'Task created'; + $listId = $this->integrationDetails->selectedList; + $apiEndpoint = $this->apiUrl . "list/{$listId}/task"; + $response = HttpHelper::post($apiEndpoint, wp_json_encode($requestParams), $this->defaultHeader); + + return empty($this->integrationDetails->attachment) ? $response : $this->uploadFile($fieldValues[$this->integrationDetails->attachment], $response->id); + } + + public function generateReqDataFromFieldMap($data, $fieldMap) + { + $dataFinal = []; + foreach ($fieldMap as $value) { + $triggerValue = $value->formField; + $actionValue = $value->clickupFormField; + if ($triggerValue === 'custom') { + if ($actionValue === 'customFieldKey') { + $dataFinal[$value->customFieldKey] = self::formatPhoneNumber(Common::replaceFieldWithValue($value->customValue, $data)); + } else { + $dataFinal[$actionValue] = self::formatPhoneNumber(Common::replaceFieldWithValue($value->customValue, $data)); + } + } elseif (!\is_null($data[$triggerValue])) { + if ($actionValue === 'customFieldKey') { + $dataFinal[$value->customFieldKey] = self::formatPhoneNumber($data[$triggerValue]); + } else { + $dataFinal[$actionValue] = self::formatPhoneNumber($data[$triggerValue]); + } + } + } + + return $dataFinal; + } + + public function execute($fieldValues, $fieldMap, $actionName) + { + $finalData = $this->generateReqDataFromFieldMap($fieldValues, $fieldMap); + if ($actionName === 'task') { + $apiResponse = $this->addTask($finalData, $fieldValues); + $apiResponse = \is_string($apiResponse) ? json_decode($apiResponse) : $apiResponse; + } + + if (!empty($apiResponse->id)) { + $res = [$this->typeName . ' successfully']; + LogHandler::save($this->integrationId, wp_json_encode(['type' => $this->type, 'type_name' => $this->typeName]), 'success', wp_json_encode($res)); + } else { + LogHandler::save($this->integrationId, wp_json_encode(['type' => $this->type, 'type_name' => $this->type . ' creating']), 'error', wp_json_encode($apiResponse)); + } + + return $apiResponse; + } + + private function uploadFile($files, $taskId) + { + $result = null; + foreach ($files as $file) { + if (\is_array($file)) { + $result = static::uploadFile($file, $taskId); + } else { + $file = Common::filePath($file); + $result = HttpHelper::post( + $this->apiUrl . "task/{$taskId}/attachment", + ['attachment' => new CURLFile($file)], + [ + 'Authorization' => $this->integrationDetails->api_key, + 'Content-Type' => 'multipart/form-data', + ] + ); + } + } + + return $result; + } + + private static function formatPhoneNumber($field) + { + if (\is_array($field) || \is_object($field) || !preg_match('/^\+?[0-9\s\-\(\)]+$/', $field)) { + return $field; + } + + $leadingPlus = $field[0] === '+' ? '+' : ''; + $cleanedNumber = preg_replace('/[^\d]/', '', $field); + + return $leadingPlus . trim($cleanedNumber); + } +} diff --git a/backend/Actions/Clickup/Routes.php b/backend/Actions/Clickup/Routes.php new file mode 100644 index 000000000..3f486e49b --- /dev/null +++ b/backend/Actions/Clickup/Routes.php @@ -0,0 +1,16 @@ +actionName; if (empty($fieldMap) || empty($authToken) || empty($actionName)) { + // translators: %s: Placeholder value return new WP_Error('REQ_FIELD_EMPTY', wp_sprintf(__('module, fields are required for %s api', 'bit-integrations'), 'ClinchPad')); } diff --git a/backend/Actions/ClinchPad/RecordApiHelper.php b/backend/Actions/ClinchPad/RecordApiHelper.php new file mode 100644 index 000000000..b15c09af4 --- /dev/null +++ b/backend/Actions/ClinchPad/RecordApiHelper.php @@ -0,0 +1,183 @@ +integrationDetails = $integrationDetails; + $this->integrationId = $integId; + $this->apiUrl = 'https://www.clinchpad.com/api/v1'; + $this->defaultHeader = [ + 'Authorization' => 'Basic ' . base64_encode("api-key:{$integrationDetails->api_key}"), + 'Content-Type' => 'application/json' + ]; + } + + public function addOrganization($finalData) + { + if (empty($finalData['name'])) { + return ['success' => false, 'message' => __('Required field Name is empty', 'bit-integrations'), 'code' => 400]; + } + + $staticFieldsKeys = ['name', 'phone', 'email', 'website', 'address']; + + foreach ($finalData as $key => $value) { + if (\in_array($key, $staticFieldsKeys)) { + $requestParams[$key] = $value; + } else { + $requestParams['fields'][] = (object) [ + '_id' => $key, + 'value' => $value, + ]; + } + } + $this->type = 'Organization'; + $this->typeName = 'Organization created'; + + $apiEndpoint = $this->apiUrl . '/organizations'; + + return $response = HttpHelper::post($apiEndpoint, wp_json_encode($requestParams), $this->defaultHeader); + } + + public function addContact($finalData) + { + if (empty($finalData['name'])) { + return ['success' => false, 'message' => __('Required field Name is empty', 'bit-integrations'), 'code' => 400]; + } + + $staticFieldsKeys = ['name', 'designation', 'phone', 'email', 'address']; + + foreach ($finalData as $key => $value) { + if (\in_array($key, $staticFieldsKeys)) { + $requestParams[$key] = $value; + } else { + $requestParams['fields'][] = (object) [ + '_id' => $key, + 'value' => $value, + ]; + } + } + + if (!empty($this->integrationDetails->actions->parentOrganization)) { + $requestParams['organization_id'] = ($this->integrationDetails->selectedParentOrganization); + } + + $this->type = 'Contact'; + $this->typeName = 'Contact created'; + + $apiEndpoint = $this->apiUrl . '/contacts'; + + return $response = HttpHelper::post($apiEndpoint, wp_json_encode($requestParams), $this->defaultHeader); + } + + public function addLead($finalData) + { + if (!isset($finalData['name'])) { + return ['success' => false, 'message' => __('Required field lead name is empty', 'bit-integrations'), 'code' => 400]; + } + $staticFieldsKeys = ['name', 'size']; + + foreach ($finalData as $key => $value) { + if (\in_array($key, $staticFieldsKeys)) { + $requestParams[$key] = $value; + } else { + $requestParams['fields'][] = (object) [ + '_id' => $key, + 'value' => $value, + ]; + } + } + + if (!empty($this->integrationDetails->selectedCRMPipeline)) { + $requestParams['pipeline_id'] = ($this->integrationDetails->selectedCRMPipeline); + } + if ($this->integrationDetails->actions->contact) { + $contactId = ($this->integrationDetails->selectedContact); + } + + $this->type = 'Lead'; + $this->typeName = 'Lead created'; + + $apiEndpoint = $this->apiUrl . '/leads'; + + $response = HttpHelper::post($apiEndpoint, wp_json_encode($requestParams), $this->defaultHeader); + $this->addContactToLead($response->_id, $contactId); + } + + public function addContactToLead($leadId, $contactId) + { + $apiEndpoint = $this->apiUrl . "/leads/{$leadId}/contacts/{$contactId}"; + + return $response = HttpHelper::post($apiEndpoint, $this->defaultHeader); + } + + public function generateReqDataFromFieldMap($data, $fieldMap) + { + $dataFinal = []; + foreach ($fieldMap as $value) { + $triggerValue = $value->formField; + $actionValue = $value->clinchPadFormField; + if ($triggerValue === 'custom') { + if ($actionValue === 'fields') { + $dataFinal[$value->customFieldKey] = Common::replaceFieldWithValue($value->customValue, $data); + } else { + $dataFinal[$actionValue] = Common::replaceFieldWithValue($value->customValue, $data); + } + } elseif (!\is_null($data[$triggerValue])) { + if ($actionValue === 'fields') { + $dataFinal[$value->customFieldKey] = $data[$triggerValue]; + } else { + $dataFinal[$actionValue] = $data[$triggerValue]; + } + } + } + + return $dataFinal; + } + + public function execute($fieldValues, $fieldMap, $actionName) + { + $finalData = $this->generateReqDataFromFieldMap($fieldValues, $fieldMap); + if ($actionName === 'organization') { + $apiResponse = $this->addOrganization($finalData); + } elseif ($actionName === 'contact') { + $apiResponse = $this->addContact($finalData); + } elseif ($actionName === 'lead') { + $apiResponse = $this->addLead($finalData); + } + + if ($apiResponse->_id) { + $res = [$this->typeName . ' successfully']; + LogHandler::save($this->integrationId, wp_json_encode(['type' => $this->type, 'type_name' => $this->typeName]), 'success', wp_json_encode($res)); + } else { + LogHandler::save($this->integrationId, wp_json_encode(['type' => $this->type, 'type_name' => $this->type . ' creating']), 'error', wp_json_encode($apiResponse)); + } + + return $apiResponse; + } +} diff --git a/backend/Actions/ClinchPad/Routes.php b/backend/Actions/ClinchPad/Routes.php new file mode 100644 index 000000000..4c2735431 --- /dev/null +++ b/backend/Actions/ClinchPad/Routes.php @@ -0,0 +1,18 @@ +actionName; if (empty($fieldMap) || empty($subDomain) || empty($actionName) || empty($apiKey)) { + // translators: %s: Placeholder value return new WP_Error('REQ_FIELD_EMPTY', wp_sprintf(__('module, fields are required for %s api', 'bit-integrations'), 'Company Hub')); } diff --git a/backend/Actions/CompanyHub/RecordApiHelper.php b/backend/Actions/CompanyHub/RecordApiHelper.php new file mode 100644 index 000000000..fcdd04113 --- /dev/null +++ b/backend/Actions/CompanyHub/RecordApiHelper.php @@ -0,0 +1,133 @@ +integrationDetails = $integrationDetails; + $this->integrationId = $integId; + $this->apiUrl = 'https://api.companyhub.com/v1'; + $this->defaultHeader = [ + 'Authorization' => "{$subDomain} {$apiKey}", + 'Content-Type' => 'application/json' + ]; + } + + public function addContact($finalData) + { + $this->type = 'Contact'; + $this->typeName = 'Contact created'; + + if (empty($finalData['LastName'])) { + return ['success' => false, 'message' => __('Required field Last Name is empty', 'bit-integrations'), 'code' => 400]; + } + if (isset($this->integrationDetails->selectedCompany) && !empty($this->integrationDetails->selectedCompany)) { + $finalData['Company'] = $this->integrationDetails->selectedCompany; + } + if (isset($this->integrationDetails->selectedSource) && !empty($this->integrationDetails->selectedSource)) { + $finalData['Source'] = $this->integrationDetails->selectedSource; + } + + $apiEndpoint = $this->apiUrl . '/tables/contact'; + + return HttpHelper::post($apiEndpoint, wp_json_encode($finalData), $this->defaultHeader); + } + + public function addCompany($finalData) + { + $this->type = 'Company'; + $this->typeName = 'Company created'; + + if (empty($finalData['Name'])) { + return ['success' => false, 'message' => __('Required field Company Name is empty', 'bit-integrations'), 'code' => 400]; + } + + $apiEndpoint = $this->apiUrl . '/tables/company'; + + return HttpHelper::post($apiEndpoint, wp_json_encode($finalData), $this->defaultHeader); + } + + public function addDeal($finalData) + { + $this->type = 'Deal'; + $this->typeName = 'Deal created'; + + if (empty($finalData['Name'])) { + return ['success' => false, 'message' => __('Required field Deal Name is empty', 'bit-integrations'), 'code' => 400]; + } + if (!isset($this->integrationDetails->selectedStage) || empty($this->integrationDetails->selectedStage)) { + return ['success' => false, 'message' => 'Required field Last Name is empty', 'code' => 400]; + } + + $finalData['Stage'] = $this->integrationDetails->selectedStage; + if (isset($this->integrationDetails->selectedCompany) && !empty($this->integrationDetails->selectedCompany)) { + $finalData['Company'] = $this->integrationDetails->selectedCompany; + } + if (isset($this->integrationDetails->selectedContact) && !empty($this->integrationDetails->selectedContact)) { + $finalData['Contact'] = $this->integrationDetails->selectedContact; + } + + $apiEndpoint = $this->apiUrl . '/tables/deal'; + + return HttpHelper::post($apiEndpoint, wp_json_encode($finalData), $this->defaultHeader); + } + + public function generateReqDataFromFieldMap($data, $fieldMap) + { + $dataFinal = []; + foreach ($fieldMap as $value) { + $triggerValue = $value->formField; + $actionValue = $value->companyHubFormField; + $dataFinal[$actionValue] = ($triggerValue === 'custom') ? Common::replaceFieldWithValue($value->customValue, $data) : $data[$triggerValue]; + } + + return $dataFinal; + } + + public function execute($fieldValues, $fieldMap, $actionName) + { + $finalData = $this->generateReqDataFromFieldMap($fieldValues, $fieldMap); + + if ($actionName === 'contact') { + $apiResponse = $this->addContact($finalData); + } elseif ($actionName === 'company') { + $apiResponse = $this->addCompany($finalData); + } elseif ($actionName === 'deal') { + $apiResponse = $this->addDeal($finalData); + } + + if ($apiResponse->Success) { + $res = [$this->typeName . ' successfully']; + LogHandler::save($this->integrationId, wp_json_encode(['type' => $this->type, 'type_name' => $this->typeName]), 'success', wp_json_encode($res)); + } else { + LogHandler::save($this->integrationId, wp_json_encode(['type' => $this->type, 'type_name' => $this->type . ' creating']), 'error', wp_json_encode($apiResponse)); + } + + return $apiResponse; + } +} diff --git a/backend/Actions/CompanyHub/Routes.php b/backend/Actions/CompanyHub/Routes.php new file mode 100644 index 000000000..a5adcbb7e --- /dev/null +++ b/backend/Actions/CompanyHub/Routes.php @@ -0,0 +1,12 @@ +_integrationDetails = $integrationDetails; + $this->_integrationID = $integId; + $this->_defaultHeader = [ + 'Authorization' => "Bearer {$this->_integrationDetails->tokenDetails->access_token}", + 'content-type' => 'application/json' + ]; + } + + public function generateReqDataFromFieldMap($data, $fieldMap) + { + $dataFinal = []; + foreach ($fieldMap as $value) { + $triggerValue = $value->formField; + $actionValue = $value->constantContactFormField; + + if ($triggerValue === 'custom') { + $dataFinal[$actionValue] = Common::replaceFieldWithValue($value->customValue, $data); + } elseif (!\is_null($data[$triggerValue])) { + $dataFinal[$actionValue] = $data[$triggerValue]; + } + } + + return $dataFinal; + } + + public function execute($listIds, $tagIds, $sourceType, $fieldValues, $fieldMap, $addressFields, $phoneFields, $addressType, $phoneType, $update) + { + $finalData = $this->generateReqDataFromFieldMap($fieldValues, $fieldMap); + + if (empty($finalData['email_address'])) { + return ['success' => false, 'message' => __('Required field Email is empty', 'bit-integrations'), 'code' => 400]; + } + + $this->mapFields($addressFields, $finalData, $fieldValues, 'street_addresses', $addressType, 'constantContactAddressField'); + $this->mapFields($phoneFields, $finalData, $fieldValues, 'phone_numbers', $phoneType, 'constantContactPhoneField'); + + $contact = $this->existContact($finalData['email_address']); + + if ($contact && !$update) { + LogHandler::save($this->_integrationID, wp_json_encode(['source_type' => 'contact', 'type_name' => 'add-contact']), 'error', __('Email already exists', 'bit-integrations')); + + return $contact; + } + + $apiResponse = $contact && $update + ? $this->updateContact($contact, $listIds, $tagIds, $sourceType, $finalData) + : $this->addContact($listIds, $tagIds, $sourceType, $finalData); + + $this->logApiResponse($apiResponse, $update, $contact); + + return $apiResponse; + } + + private function updateContact($contact, $listIds, $tagIds, $sourceType, $finalData) + { + return $this->sendContactRequest('contacts/' . $contact->contact_id, $listIds, $tagIds, $sourceType, $finalData, 'update_source', $contact); + } + + private function addContact($listIds, $tagIds, $sourceType, $finalData) + { + return $this->sendContactRequest('contacts', $listIds, $tagIds, $sourceType, $finalData, 'create_source'); + } + + private function sendContactRequest($endpoint, $listIds, $tagIds, $sourceType, $finalData, $sourceKey, $contact = null) + { + $requestParams = [ + 'email_address' => (object) ['address' => $finalData['email_address']], + $sourceKey => $sourceType, + 'list_memberships' => $this->splitValues($listIds), + 'taggings' => $this->splitValues($tagIds), + ]; + + $apiEndpoint = $this->baseUrl . $endpoint; + $requestParams = $this->prepareCustomFields($finalData, $requestParams); + $method = $sourceKey === 'create_source' ? 'post' : 'put'; + + if ($sourceKey !== 'create_source' && $contact) { + $requestParams['taggings'] = array_unique(array_merge($requestParams['taggings'], $contact->taggings ?? [])); + $requestParams['list_memberships'] = array_unique(array_merge($requestParams['list_memberships'], $contact->list_memberships ?? [])); + $this->mergeExistingFields($requestParams, $contact); + + foreach ((array) $contact as $key => $value) { + if (empty($requestParams[$key]) && !\in_array($key, ['contact_id', 'email_address', 'update_source', 'create_source', 'created_at', 'updated_at', 'custom_fields', 'phone_numbers', 'street_addresses', 'list_memberships', 'taggings', 'notes'])) { + $requestParams[$key] = $value; + } + } + } + + return HttpHelper::$method($apiEndpoint, wp_json_encode((object) $requestParams), $this->_defaultHeader); + } + + private function mergeExistingFields(&$requestParams, $contact) + { + $this->mergeCustomFields($requestParams, $contact); + $this->mergePhoneNumbers($requestParams, $contact); + $this->mergeStreetAddresses($requestParams, $contact); + } + + private function mergeCustomFields(&$requestParams, $contact) + { + $existingCustomFields = array_column($contact->custom_fields ?? [], 'value', 'custom_field_id'); + foreach ($existingCustomFields as $key => $value) { + if (!isset($requestParams['custom_fields']) || !\array_key_exists($key, $requestParams['custom_fields'])) { + $requestParams['custom_fields'][] = ['custom_field_id' => $key, 'value' => $value]; + } + } + } + + private function mergePhoneNumbers(&$requestParams, $contact) + { + foreach ($contact->phone_numbers ?? [] as $number) { + if (!isset($requestParams['phone_numbers']) || $requestParams['phone_numbers'][0]['kind'] != $number->kind) { + $requestParams['phone_numbers'][] = ['kind' => $number->kind, 'phone_number' => $number->phone_number]; + } + } + } + + private function mergeStreetAddresses(&$requestParams, $contact) + { + foreach ($contact->street_addresses ?? [] as $address) { + if (!isset($requestParams['street_addresses']) || $requestParams['street_addresses'][0]['kind'] != $address->kind) { + $requestParams['street_addresses'][] = [ + 'kind' => $address->kind, + 'street' => $address->street ?? '', + 'city' => $address->city ?? '', + 'state' => $address->state ?? '', + 'postal_code' => $address->postal_code ?? '', + 'country' => $address->country ?? '', + ]; + } elseif ($requestParams['street_addresses'][0]['kind'] === $address->kind) { + $this->fillEmptyAddressFields($requestParams['street_addresses'][0], $address); + } + } + } + + private function fillEmptyAddressFields(&$target, $source) + { + $fields = ['street', 'city', 'state', 'postal_code', 'country']; + foreach ($fields as $field) { + $target[$field] = empty($target[$field]) ? $source->{$field} : $target[$field]; + } + } + + private function splitValues($values) + { + return !empty($values) ? explode(',', $values) : []; + } + + private function prepareCustomFields($data, $requestParams) + { + $customFields = []; + foreach ($data as $key => $value) { + if ($key !== 'email_address') { + if (str_contains($key, 'custom-')) { + $customFields[] = [ + 'custom_field_id' => str_replace('custom-', '', $key), + 'value' => $value, + ]; + } else { + $requestParams[$key] = $value; + } + } + } + + $requestParams['custom_fields'] = $customFields; + + return $requestParams; + } + + private function logApiResponse($apiResponse, $update, $contactId = null) + { + $type = $update && $contactId ? 'update-contact' : 'add-contact'; + + if (is_wp_error($apiResponse) || empty($apiResponse->contact_id) || isset($apiResponse->error_key)) { + $logLevel = 'error'; + } else { + $logLevel = 'success'; + } + + LogHandler::save($this->_integrationID, wp_json_encode(['source_type' => 'contact', 'type_name' => $type]), $logLevel, wp_json_encode($apiResponse)); + } + + private function mapFields($fields, &$finalData, $fieldValues, $key, $type, $formFieldKey) + { + if (!empty($fields)) { + $mappedFields = []; + foreach ($fields as $field) { + $mappedFields[$field->{$formFieldKey}] = $field->formField === 'custom' + ? Common::replaceFieldWithValue($field->customValue, $fieldValues) + : ($fieldValues[$field->formField] ?? null); + } + $mappedFields['kind'] = $type; + $finalData[$key] = [$mappedFields]; + } + } + + private function existContact($email) + { + $apiEndpoints = $apiEndpoints = $this->baseUrl . 'contacts?email=' . $email . '&include=custom_fields,list_memberships,taggings,notes,phone_numbers,street_addresses,sms_channel'; + $apiResponse = HttpHelper::get($apiEndpoints, null, $this->_defaultHeader); + + if (is_wp_error($apiResponse) || empty($apiResponse->contacts) || isset($apiResponse->error_key) || (\gettype($apiResponse) === 'array' && $apiResponse[0]->error_key)) { + return false; + } + + return $apiResponse->contacts[0] ?? false; + } +} diff --git a/backend/Actions/ConstantContact/Routes.php b/backend/Actions/ConstantContact/Routes.php new file mode 100644 index 000000000..997158122 --- /dev/null +++ b/backend/Actions/ConstantContact/Routes.php @@ -0,0 +1,14 @@ +_integrationID); diff --git a/backend/Actions/ConvertKit/RecordApiHelper.php b/backend/Actions/ConvertKit/RecordApiHelper.php new file mode 100644 index 000000000..9feeaf0a3 --- /dev/null +++ b/backend/Actions/ConvertKit/RecordApiHelper.php @@ -0,0 +1,210 @@ +_integrationDetails = $integrationDetails; + $this->_defaultHeader = $api_secret; + $this->_apiEndpoint = 'https://api.convertkit.com/v3'; + $this->_integrationID = $integId; + } + + // for adding a subscriber + public function storeOrModifyRecord($method, $formId, $data) + { + $queries = $this->httpBuildQuery($data); + $insertRecordEndpoint = "{$this->_apiEndpoint}/forms/{$formId}/{$method}?{$queries}"; + + return HttpHelper::post($insertRecordEndpoint, null); + } + + // for updating subscribers data through email id. + public function updateRecord($id, $data) + { + $queries = $this->httpBuildQuery($data); + $updateRecordEndpoint = "{$this->_apiEndpoint}/subscribers/{$id}?" . $queries; + + return HttpHelper::request($updateRecordEndpoint, 'PUT', null); + } + + public function addTagToSubscriber($email, $tags) + { + $queries = http_build_query([ + 'api_secret' => $this->_defaultHeader, + 'email' => $email, + ]); + + foreach ($tags as $tagId) { + $searchEndPoint = "{$this->_apiEndpoint}/tags/{$tagId}/subscribe?{$queries}"; + $recordApiResponse = HttpHelper::post($searchEndPoint, null); + } + + return $recordApiResponse; + } + + public function removeTagToSubscriber($email, $tags) + { + $queries = http_build_query([ + 'api_secret' => $this->_defaultHeader, + 'email' => $email, + ]); + + foreach ($tags as $tagId) { + $searchEndPoint = "{$this->_apiEndpoint}/tags/{$tagId}/unsubscribe?{$queries}"; + $recordApiResponse = HttpHelper::post($searchEndPoint, null); + } + + return $recordApiResponse; + } + + public function execute($fieldValues, $fieldMap, $actions, $formId, $tags) + { + $convertKit = (object) $this->setFieldMapping($fieldMap, $fieldValues); + $module = empty($this->_integrationDetails->module) ? 'add_subscriber_to_a_form' : $this->_integrationDetails->module; + $existSubscriber = !empty($actions->update) ? $this->existSubscriber($convertKit->email) : false; + $type = $typeName = null; + $recordApiResponse = null; + + switch ($module) { + case 'add_subscriber_to_a_form': + if (!empty($actions->update) && !empty($existSubscriber)) { + $recordApiResponse = $this->updateRecord($existSubscriber->id, $convertKit); + $typeName = 'update'; + } elseif (empty($existSubscriber)) { + $recordApiResponse = $this->storeOrModifyRecord('subscribe', $formId, $convertKit); + $typeName = 'insert'; + } else { + $recordApiResponse = (object) ['error' => __('Email address already exists in the system', 'bit-integrations')]; + $typeName = 'insert'; + } + if (isset($tags) && (\count($tags)) > 0 && isset($recordApiResponse) && !isset($recordApiResponse->error)) { + $this->addTagToSubscriber($convertKit->email, $tags); + } + + $type = 'Add subscriber to a form'; + + break; + + case 'update_a_subscriber': + $recordApiResponse = $existSubscriber ? $this->updateRecord($existSubscriber->id, $convertKit) : (object) ['error' => 'Subscriber not found!']; + + if (isset($tags) && (\count($tags)) > 0 && isset($recordApiResponse) && !isset($recordApiResponse->error)) { + $this->addTagToSubscriber($convertKit->email, $tags); + } + + $type = 'Update subscriber'; + $typeName = 'update'; + + break; + + case 'add_tags_to_a_subscriber': + $recordApiResponse = $this->addTagToSubscriber($convertKit->email, $tags); + $type = 'Add tags to subscriber'; + $typeName = 'insert'; + + break; + + case 'remove_tags_to_a_subscriber': + $recordApiResponse = $this->removeTagToSubscriber($convertKit->email, $tags); + $type = 'Remove tags from subscriber'; + $typeName = 'insert'; + + break; + } + + if (isset($existSubscriber->error)) { + LogHandler::save($this->_integrationID, ['type' => $type, 'type_name' => 'insert'], 'error', $existSubscriber->error); + } elseif ($recordApiResponse && isset($recordApiResponse->error)) { + LogHandler::save($this->_integrationID, ['type' => $type, 'type_name' => $typeName], 'error', $recordApiResponse->error); + } else { + LogHandler::save($this->_integrationID, ['type' => $type, 'type_name' => $typeName], 'success', $recordApiResponse); + } + + return $recordApiResponse; + } + + private function setFieldMapping($fieldMap, $fieldValues) + { + $fieldData = []; + $customFields = []; + + foreach ($fieldMap as $fieldKey => $fieldPair) { + if (!empty($fieldPair->convertKitField)) { + if ($fieldPair->formField === 'custom' && isset($fieldPair->customValue) && !is_numeric($fieldPair->convertKitField)) { + $fieldData[$fieldPair->convertKitField] = Common::replaceFieldWithValue($fieldPair->customValue, $fieldValues); + } elseif (is_numeric($fieldPair->convertKitField) && $fieldPair->formField === 'custom' && isset($fieldPair->customValue)) { + $customFields[] = ['field' => (int) $fieldPair->convertKitField, 'value' => Common::replaceFieldWithValue($fieldPair->customValue, $fieldValues)]; + } elseif (is_numeric($fieldPair->convertKitField)) { + $customFields[] = ['field' => (int) $fieldPair->convertKitField, 'value' => $fieldValues[$fieldPair->formField]]; + } else { + $fieldData[$fieldPair->convertKitField] = $fieldValues[$fieldPair->formField]; + } + } + } + + if (!empty($customFields)) { + $fieldData['fieldValues'] = $customFields; + } + + return $fieldData; + } + + private function httpBuildQuery($data) + { + $query = [ + 'api_secret' => $this->_defaultHeader, + 'email' => $data->email, + 'first_name' => $data->firstName, + ]; + + foreach ($data as $key => $value) { + $key = strtolower(preg_replace('/(? $this->_defaultHeader, + 'email_address' => $email, + 'page' => 1, + 'status' => 'all', + ]); + + $response = HttpHelper::get("{$this->_apiEndpoint}/subscribers?{$queries}", null); + + if (is_wp_error($response) || empty($response->subscribers)) { + return false; + } + + return \is_array($response->subscribers) && \count($response->subscribers) > 0 ? $response->subscribers[0] : $response->subscribers; + } +} diff --git a/backend/Actions/ConvertKit/Routes.php b/backend/Actions/ConvertKit/Routes.php new file mode 100644 index 000000000..b49f71dbf --- /dev/null +++ b/backend/Actions/ConvertKit/Routes.php @@ -0,0 +1,13 @@ +actionName; if (empty($fieldMap) || empty($authToken) || empty($actionName)) { + // translators: %s: Placeholder value return new WP_Error('REQ_FIELD_EMPTY', wp_sprintf(__('module, fields are required for %s api', 'bit-integrations'), 'Copper CRM')); } diff --git a/backend/Actions/CopperCRM/RecordApiHelper.php b/backend/Actions/CopperCRM/RecordApiHelper.php new file mode 100644 index 000000000..9cb045237 --- /dev/null +++ b/backend/Actions/CopperCRM/RecordApiHelper.php @@ -0,0 +1,301 @@ +integrationDetails = $integrationDetails; + $this->integrationId = $integId; + $this->apiEmail = 'https://api.copper.com/developer_api/v1'; + $this->defaultHeader = [ + 'X-PW-AccessToken' => $integrationDetails->api_key, + 'X-PW-Application' => 'developer_api', + 'X-PW-UserEmail' => $integrationDetails->api_email, + 'Content-Type' => 'application/json' + ]; + } + + public function addCompany($finalData) + { + if (empty($finalData['name'])) { + return ['success' => false, 'message' => __('Required field Name is empty', 'bit-integrations'), 'code' => 400]; + } + + $staticFieldsKeys = ['name', 'email_domain', 'details', 'street', 'city', 'state', 'postal_code', 'country', 'phone_numbers', 'websites']; + + foreach ($finalData as $key => $value) { + if (\in_array($key, $staticFieldsKeys)) { + if (($key == 'street' || $key == 'city' || $key == 'state' || $key == 'postal_code' || $key == 'country')) { + $requestParams['address'][$key] = $value; + } elseif (($key == 'websites')) { + $requestParams['websites'][] = (object) [ + 'url' => $value, + 'category' => 'work' + ]; + } elseif ($key == 'phone_numbers') { + $requestParams[$key][] = (object) [ + 'number' => $value, + ]; + } else { + $requestParams[$key] = $value; + } + } else { + $requestParams['custom_fields'][] = (object) [ + 'value' => $value, + 'custom_field_definition_id' => $this->getCustomFieldId($key, 'company') + ]; + } + } + + if ($this->integrationDetails->actions->owner) { + $requestParams['assignee_id'] = (int) ($this->integrationDetails->selectedOwner); + } + + $this->type = 'Company'; + $this->typeName = 'Company created'; + + $apiEndpoint = $this->apiEmail . '/companies'; + + return HttpHelper::post($apiEndpoint, wp_json_encode($requestParams), $this->defaultHeader); + } + + public function addPerson($finalData) + { + if (empty($finalData['name'])) { + return ['success' => false, 'message' => __('Required field Name is empty', 'bit-integrations'), 'code' => 400]; + } + + $staticFieldsKeys = ['name', 'title', 'details', 'email', 'email_domain', 'phone_numbers', 'street', 'city', 'state', 'postal_code', 'country', 'websites']; + + foreach ($finalData as $key => $value) { + if (\in_array($key, $staticFieldsKeys)) { + if (($key == 'street' || $key == 'city' || $key == 'state' || $key == 'postal_code' || $key == 'country')) { + $requestParams['address'][$key] = $value; + } elseif (($key == 'websites')) { + $requestParams['websites'][] = (object) [ + 'url' => $value, + 'category' => 'work' + ]; + } elseif ($key === 'email' || $key === 'email_domain') { + $requestParams['emails'][] = (object) [ + 'email' => $value, + 'category' => 'work' + ]; + } elseif ($key == 'phone_numbers') { + $requestParams[$key][] = (object) [ + 'number' => $value, + ]; + } else { + $requestParams[$key] = $value; + } + } else { + $requestParams['custom_fields'][] = (object) [ + 'value' => $value, + 'custom_field_definition_id' => $this->getCustomFieldId($key, 'person') + ]; + } + } + + if (isset($this->integrationDetails->selectedOwner)) { + $requestParams['assignee_id'] = (int) ($this->integrationDetails->selectedOwner); + } + if (isset($this->integrationDetails->selectedCompany)) { + $requestParams['company_id'] = (int) ($this->integrationDetails->selectedCompany); + } + if (isset($this->integrationDetails->selectedTags)) { + $requestParams['tags'] = explode(',', $this->integrationDetails->selectedTags); + } + + $this->type = 'Person'; + $this->typeName = 'Person created'; + + $apiEndpoint = $this->apiEmail . '/people'; + + return HttpHelper::post($apiEndpoint, wp_json_encode($requestParams), $this->defaultHeader); + } + + public function addOpportunity($finalData) + { + if (empty($finalData['name'])) { + return ['success' => false, 'message' => __('Required field opportunity name is empty', 'bit-integrations'), 'code' => 400]; + } + $staticFieldsKeys = ['name', 'close_date', 'details', 'monetary_value']; + + foreach ($finalData as $key => $value) { + if (\in_array($key, $staticFieldsKeys)) { + if ($key == 'close_date') { + $requestParams['close_date'] = gmdate('m/d/Y', strtotime($value)); + } else { + $requestParams[$key] = $value; + } + } else { + $requestParams['custom_fields'][] = (object) [ + 'value' => $value, + 'custom_field_definition_id' => $this->getCustomFieldId($key, 'opportunity') + ]; + } + } + + if (isset($this->integrationDetails->selectedCRMPeople)) { + $requestParams['primary_contact_id'] = (int) ($this->integrationDetails->selectedCRMPeople); + } + if (isset($this->integrationDetails->selectedCRMPipelines)) { + $requestParams['pipeline_id'] = (int) ($this->integrationDetails->selectedCRMPipelines); + } + if ($this->integrationDetails->actions->owner) { + $requestParams['assignee_id'] = (int) ($this->integrationDetails->selectedOwner); + } + if ($this->integrationDetails->actions->company) { + $requestParams['company_id'] = (int) ($this->integrationDetails->selectedCompany); + } + if (isset($this->integrationDetails->actions->pipelineStage)) { + $requestParams['pipeline_stage_id'] = (int) ($this->integrationDetails->selectedPipelineStage); + } + + $this->type = 'Opportunity'; + $this->typeName = 'Opportunity created'; + + $apiEndpoint = $this->apiEmail . '/opportunities'; + + return HttpHelper::post($apiEndpoint, wp_json_encode($requestParams), $this->defaultHeader); + } + + public function addTask($finalData) + { + if (empty($finalData['name'])) { + return ['success' => false, 'message' => __('Required field task name is empty', 'bit-integrations'), 'code' => 400]; + } + + $staticFieldsKeys = ['name', 'due_date', 'reminder_date', 'details']; + + foreach ($finalData as $key => $value) { + if (\in_array($key, $staticFieldsKeys)) { + if ($key == 'due_date' || $key == 'reminder_date') { + $requestParams[$key] = strtotime($value); + } else { + $requestParams[$key] = $value; + } + } else { + $requestParams['custom_fields'][] = (object) [ + 'value' => $value, + 'custom_field_definition_id' => $this->getCustomFieldId($key, 'task') + ]; + } + } + + if ($this->integrationDetails->actions->owner) { + $requestParams['assignee_id'] = (int) ($this->integrationDetails->selectedOwner); + } + + $this->type = 'Task'; + $this->typeName = 'Task created'; + + $apiEndpoint = $this->apiEmail . '/tasks'; + + return HttpHelper::post($apiEndpoint, wp_json_encode($requestParams), $this->defaultHeader); + } + + public function generateReqDataFromFieldMap($data, $fieldMap) + { + $dataFinal = []; + foreach ($fieldMap as $value) { + $triggerValue = $value->formField === 'custom' && isset($value->customValue) + ? Common::replaceFieldWithValue($value->customValue, $data) + : $value->formField; + + $actionValue = $value->coppercrmFormField === 'customFieldKey' && isset($value->customFieldKey) + ? Common::replaceFieldWithValue($value->customFieldKey, $data) + : $value->coppercrmFormField; + + $dataFinal[$actionValue] = $data[$triggerValue]; + } + + return $dataFinal; + } + + public function execute($fieldValues, $fieldMap, $actionName) + { + $finalData = $this->generateReqDataFromFieldMap($fieldValues, $fieldMap); + if ($actionName === 'company') { + $apiResponse = $this->addCompany($finalData); + } elseif ($actionName === 'person') { + $apiResponse = $this->addPerson($finalData); + } elseif ($actionName === 'opportunity') { + $apiResponse = $this->addOpportunity($finalData); + } elseif ($actionName === 'task') { + $apiResponse = $this->addTask($finalData); + } + + if ($apiResponse->id || $apiResponse->status === 'success') { + $res = [$this->typeName . ' successfully']; + LogHandler::save($this->integrationId, wp_json_encode(['type' => $this->type, 'type_name' => $this->typeName]), 'success', wp_json_encode($res)); + } else { + LogHandler::save($this->integrationId, wp_json_encode(['type' => $this->type, 'type_name' => $this->type . ' creating']), 'error', wp_json_encode($apiResponse)); + } + + return $apiResponse; + } + + private function getCustomFieldId($key, $module) + { + $apiEndpoint = $this->apiEmail . '/custom_field_definitions'; + + $customFields = HttpHelper::get($apiEndpoint, null, $this->defaultHeader); + + foreach ($customFields as $field) { + if ($field->name === $key && \in_array($module, $field->available_on)) { + return $field->id; + } + } + + $body = [ + 'name' => $key, + 'data_type' => 'String', + 'available_on' => [$module] + ]; + + $fieldDefinition = HttpHelper::post($apiEndpoint, wp_json_encode($body), $this->defaultHeader); + + $status = $fieldDefinition->id ? 'success' : 'error'; + $message = $fieldDefinition->id ? 'Custom field created successfully' : 'Custom field creation failed'; + + LogHandler::save( + $this->integrationId, + wp_json_encode( + [ + 'type' => 'Create Custom Field', + 'type_name' => $message + ] + ), + $status, + wp_json_encode($fieldDefinition) + ); + + return $fieldDefinition->id ?? $key; + } +} diff --git a/backend/Actions/CopperCRM/Routes.php b/backend/Actions/CopperCRM/Routes.php new file mode 100644 index 000000000..cea1b67d7 --- /dev/null +++ b/backend/Actions/CopperCRM/Routes.php @@ -0,0 +1,18 @@ +flow_details->funcFileLocation; + $integId = $integrationData->id; + $isExits = file_exists($funcFileLocation); + $isSuccessfullyRun = true; + $additionalData = null; + + ob_start(); + if ($isExits) { + $trigger = (array) $fieldValues; + + try { + include "{$funcFileLocation}"; + } catch (Throwable $th) { + $isSuccessfullyRun = false; + LogHandler::save($integId, $th->getMessage(), 'error', __('Custom action Failed', 'bit-integrations')); + } + $additionalData = ob_get_clean(); + } else { + LogHandler::save($integId, wp_json_encode(['type' => 'custom_action', 'type_name' => 'custom action']), 'error', wp_json_encode('Custom action file not found')); + + return; + } + if ($isSuccessfullyRun) { + LogHandler::save($integId, wp_json_encode(['type' => 'custom_action', 'type_name' => 'custom action']), 'success', wp_json_encode('Custom action successfully run' . !empty($additionalData) ? wp_json_encode($additionalData) : '')); + } + + return true; + } +} diff --git a/backend/Actions/CustomAction/Routes.php b/backend/Actions/CustomAction/Routes.php new file mode 100644 index 000000000..14aaace63 --- /dev/null +++ b/backend/Actions/CustomAction/Routes.php @@ -0,0 +1,10 @@ +actionName; if (empty($fieldMap) || empty($apiSecret) || empty($actionName) || empty($apiKey)) { + // translators: %s: Placeholder value return new WP_Error('REQ_FIELD_EMPTY', wp_sprintf(__('module, fields are required for %s api', 'bit-integrations'), 'Demio')); } diff --git a/backend/Actions/Demio/RecordApiHelper.php b/backend/Actions/Demio/RecordApiHelper.php new file mode 100644 index 000000000..4bb503962 --- /dev/null +++ b/backend/Actions/Demio/RecordApiHelper.php @@ -0,0 +1,93 @@ +integrationDetails = $integrationDetails; + $this->integrationId = $integId; + $this->apiUrl = 'https://my.demio.com/api/v1'; + $this->defaultHeader = [ + 'Api-Key' => $apiKey, + 'Api-Secret' => $apiSecret, + 'Content-Type' => 'application/json' + ]; + } + + public function registration($finalData) + { + $this->type = 'Register People to Wabinar'; + $this->typeName = 'Register People to Wabinar'; + + if (empty($finalData['name'])) { + return ['success' => false, 'message' => __('Required field First Name is empty', 'bit-integrations'), 'code' => 400]; + } + if (empty($finalData['email'])) { + return ['success' => false, 'message' => __('Required field Email is empty', 'bit-integrations'), 'code' => 400]; + } + if (!isset($this->integrationDetails->selectedEvent) || empty($this->integrationDetails->selectedEvent)) { + return ['success' => false, 'message' => __('Required field Event is empty', 'bit-integrations'), 'code' => 400]; + } + if (isset($this->integrationDetails->selectedEvent) || !empty($this->integrationDetails->selectedEvent)) { + $finalData['id'] = $this->integrationDetails->selectedEvent; + } + if (isset($this->integrationDetails->selectedSession) && !empty($this->integrationDetails->selectedSession)) { + $finalData['date_id'] = $this->integrationDetails->selectedSession; + } + + $apiEndpoint = $this->apiUrl . '/event/register'; + + return HttpHelper::post($apiEndpoint, wp_json_encode($finalData), $this->defaultHeader); + } + + public function generateReqDataFromFieldMap($data, $fieldMap) + { + $dataFinal = []; + foreach ($fieldMap as $value) { + $triggerValue = $value->formField; + $actionValue = $value->demioFormField; + $dataFinal[$actionValue] = ($triggerValue === 'custom') ? $value->customValue : $data[$triggerValue]; + } + + return $dataFinal; + } + + public function execute($fieldValues, $fieldMap, $actionName) + { + $finalData = $this->generateReqDataFromFieldMap($fieldValues, $fieldMap); + $apiResponse = $this->registration($finalData); + + if (!isset($apiResponse->errors)) { + $res = [$this->typeName . ' successfully']; + LogHandler::save($this->integrationId, wp_json_encode(['type' => $this->type, 'type_name' => $this->typeName]), 'success', wp_json_encode($res)); + } else { + LogHandler::save($this->integrationId, wp_json_encode(['type' => $this->type, 'type_name' => $this->type . ' creating']), 'error', wp_json_encode($apiResponse)); + } + + return $apiResponse; + } +} diff --git a/backend/Actions/Demio/Routes.php b/backend/Actions/Demio/Routes.php new file mode 100644 index 000000000..8717e7db9 --- /dev/null +++ b/backend/Actions/Demio/Routes.php @@ -0,0 +1,12 @@ +_integrationID); diff --git a/backend/Actions/DirectIq/RecordApiHelper.php b/backend/Actions/DirectIq/RecordApiHelper.php new file mode 100644 index 000000000..678af3e76 --- /dev/null +++ b/backend/Actions/DirectIq/RecordApiHelper.php @@ -0,0 +1,89 @@ +_defaultHeader = 'Basic ' . base64_encode("{$client_id}:{$client_secret}"); + $this->_integrationID = $integId; + } + + // for adding a contact to a list. + public function storeOrModifyRecord($method, $listId, $data) + { + $apiEndpoint = "https://rest.directiq.com/contacts/lists/importcontacts/{$listId}"; + $headers = [ + 'accept' => 'application/json', + 'Authorization' => $this->_defaultHeader, + 'content-type' => 'application/*+json' + ]; + $finalData = [ + 'contacts' => [ + [ + 'email' => $data->email ?? '', + 'fistName' => $data->first_name ?? '', + 'lastName' => $data->last_name ?? '' + ], + ] + ]; + + HttpHelper::post($apiEndpoint, wp_json_encode($finalData), $headers); + + return HttpHelper::$responseCode; + } + + public function generateReqDataFromFieldMap($data, $fieldMap) + { + $dataFinal = []; + + foreach ($fieldMap as $key => $value) { + $triggerValue = $value->formField; + $actionValue = $value->directIqField; + if ($triggerValue === 'custom') { + $dataFinal[$actionValue] = $value->customValue; + } elseif (!\is_null($data[$triggerValue])) { + $dataFinal[$actionValue] = $data[$triggerValue]; + } + } + + return $dataFinal; + } + + public function execute($fieldValues, $fieldMap, $actions, $listId) + { + $fieldData = []; + $customFields = []; + + $finalData = $this->generateReqDataFromFieldMap($fieldValues, $fieldMap); + + $directIq = (object) $finalData; + + $recordApiResponse = $this->storeOrModifyRecord('contact', $listId, $directIq); + + $type = 'insert'; + + if ($recordApiResponse !== 200) { + LogHandler::save($this->_integrationID, ['type' => 'record', 'type_name' => $type], 'error', __('There is an error while inserting record', 'bit-integrations')); + } else { + LogHandler::save($this->_integrationID, ['type' => 'record', 'type_name' => $type], 'success', __('Record inserted successfully', 'bit-integrations')); + } + + return $recordApiResponse; + } +} diff --git a/backend/Actions/DirectIq/Routes.php b/backend/Actions/DirectIq/Routes.php new file mode 100644 index 000000000..030b4ea04 --- /dev/null +++ b/backend/Actions/DirectIq/Routes.php @@ -0,0 +1,12 @@ +_payloadBoundary = wp_generate_password(24); + $this->_defaultHeader['Content-Type'] = 'multipart/form-data; boundary=' . $this->_payloadBoundary; + } + + /** + * Helps to execute upload files api + * + * @param string $apiEndPoint discord API base URL + * @param array $data Data to pass to API + * @param mixed $_accessToken + * @param mixed $channel_id + * + * @return array $uploadResponse discord API response + */ + public function uploadFiles($apiEndPoint, $data, $_accessToken, $channel_id) + { + $uploadFileEndpoint = $apiEndPoint . '/channels/' . $channel_id . '/messages'; + + if (\is_array($data['file'])) { + $file = $data['file'][0]; + } else { + $file = $data['file']; + } + + if (empty($file)) { + return false; + } + + return HttpHelper::post( + $uploadFileEndpoint, + [ + 'filename' => new CURLFile($file) + ], + [ + 'Content-Type' => 'multipart/form-data', + 'Authorization' => 'Bot ' . $_accessToken + ] + ); + } +} diff --git a/backend/Actions/Discord/RecordApiHelper.php b/backend/Actions/Discord/RecordApiHelper.php new file mode 100644 index 000000000..5117be619 --- /dev/null +++ b/backend/Actions/Discord/RecordApiHelper.php @@ -0,0 +1,102 @@ +_defaultHeader['Content-Type'] = 'multipart/form-data'; + $this->_defaultHeader['Content-Disposition'] = 'form-data'; + $this->_integrationID = $integId; + $this->_apiEndPoint = $apiEndPoint; + $this->_accessToken = $access_token; + } + + public function sendMessages($data, $channel_id) + { + $header = [ + 'Authorization' => 'Bot ' . $this->_accessToken, + 'Accept' => 'application/json', + ]; + + $insertRecordEndpoint = $this->_apiEndPoint . '/channels/' . $channel_id . '/messages'; + + return HttpHelper::post($insertRecordEndpoint, $data, $header); + } + + public function execute($integrationDetails, $fieldValues) + { + $msg = Common::replaceFieldWithValue($integrationDetails->body, $fieldValues); + $messagesBody = str_replace(['

', '

'], ' ', $msg); + + $recordApiResponse = ''; + if (!empty($integrationDetails->actions->attachments)) { + foreach ($fieldValues as $fieldKey => $fieldValue) { + if ($integrationDetails->actions->attachments == $fieldKey) { + $file = $fieldValue; + } + } + + if ( + !empty($file) + && ( + (\is_array($file)) + ) + ) { + $data = [ + 'content' => $messagesBody, + 'parse_mode' => $integrationDetails->parse_mode, + 'file' => \is_array($file) ? $file[0] : $file + ]; + + $sendPhotoApiHelper = new FilesApiHelper($this->_accessToken); + $recordApiResponse = $sendPhotoApiHelper->uploadFiles($this->_apiEndPoint, $data, $this->_accessToken, $integrationDetails->selectedChannel); + $recordApiResponse = $this->sendMessages($data, $integrationDetails->selectedChannel); + } else { + $data = [ + 'content' => $messagesBody, + 'parse_mode' => $integrationDetails->parse_mode + ]; + $recordApiResponse = $this->sendMessages($data, $integrationDetails->selectedChannel); + } + $type = 'insert'; + } else { + $data = [ + 'content' => $messagesBody, + 'parse_mode' => $integrationDetails->parse_mode + ]; + + $recordApiResponse = $this->sendMessages($data, $integrationDetails->selectedChannel); + $type = 'insert'; + } + $recordApiResponse = \is_string($recordApiResponse) ? json_decode($recordApiResponse) : $recordApiResponse; + + if (isset($recordApiResponse->id)) { + LogHandler::save($this->_integrationID, ['type' => 'record', 'type_name' => $type], 'success', $recordApiResponse); + } else { + LogHandler::save($this->_integrationID, ['type' => 'record', 'type_name' => $type], 'error', $recordApiResponse); + } + + return $recordApiResponse; + } +} diff --git a/backend/Actions/Discord/Routes.php b/backend/Actions/Discord/Routes.php new file mode 100644 index 000000000..90262fe94 --- /dev/null +++ b/backend/Actions/Discord/Routes.php @@ -0,0 +1,13 @@ +_integrationID = $integId; + } + + public function createVendor($finalData, $actions) + { + if (empty($finalData['email']) || empty($finalData['user_login']) || empty($finalData['store_name'])) { + return ['success' => false, 'message' => __('Required field email, username or store name is empty!', 'bit-integrations'), 'code' => 400]; + } + + $data = $this->formatVendorUpsertData($finalData, $actions, 'createVendor'); + + $store = dokan()->vendor->create($data); + + if (is_wp_error($store)) { + return ['success' => false, 'message' => $store->get_error_message(), 'code' => 400]; + } + + return ['success' => true, 'message' => __('Vendor created successfully.', 'bit-integrations')]; + } + + public function updateVendor($finalData, $selectedVendor, $actions) + { + if (empty($selectedVendor)) { + return ['success' => false, 'message' => __('Required field vendor is empty!', 'bit-integrations'), 'code' => 400]; + } + + $data = $this->formatVendorUpsertData($finalData, $actions, 'updateVendor'); + + $storeId = dokan()->vendor->update($selectedVendor, $data); + + if (is_wp_error($storeId)) { + return ['success' => false, 'message' => $storeId->get_error_message(), 'code' => 400]; + } + + return ['success' => true, 'message' => __('Vendor updated successfully.', 'bit-integrations')]; + } + + public function formatVendorUpsertData($finalData, $actions, $module) + { + $paymentBankKeys = ['payment_bank_ac_name', 'payment_bank_ac_type', 'payment_bank_ac_number', 'payment_bank_bank_name', 'payment_bank_bank_addr', + 'payment_bank_routing_number', 'payment_bank_iban', 'payment_bank_swift']; + + $addressKeys = ['street_1', 'street_2', 'city', 'zip', 'state', 'country']; + $euFieldsKey = ['dokan_company_name', 'dokan_company_id_number', 'dokan_vat_number', 'dokan_bank_name', 'dokan_bank_iban']; + $data = []; + + foreach ($finalData as $key => $value) { + if ($key === 'payment_paypal_email') { + $data['payment']['paypal'] = ['email' => $value]; + } elseif (\in_array($key, $paymentBankKeys)) { + $data['payment']['bank'][str_replace('payment_bank_', '', $key)] = $value; + } elseif (\in_array($key, $addressKeys)) { + $data['address'][$key] = $value; + } elseif (\in_array($key, $euFieldsKey)) { + $data[str_replace('dokan_', '', $key)] = $value; + } else { + $data[$key] = $value; + } + } + + $filterResponse = Hooks::apply(Config::withPrefix('dokan_vendor_crud_actions'), $module, $actions); + + /** + * @deprecated 2.7.8 Use `bit_integrations_dokan_vendor_crud_actions` filter instead. + * @since 2.7.8 + */ + $filterResponse = Hooks::apply('btcbi_dokan_vendor_crud_actions', $filterResponse, $module, $actions); + + if ($filterResponse !== $module && !empty($filterResponse)) { + $data = array_merge($data, $filterResponse); + } + + return $data; + } + + public function deleteVendor($finalData, $selectedVendor) + { + if (empty($finalData['vendor_email']) && empty($selectedVendor)) { + return ['success' => false, 'message' => __('Vendor email or id is required!', 'bit-integrations'), 'code' => 400]; + } + + if (!empty($selectedVendor)) { + $vendorId = $selectedVendor; + } else { + $vendorId = self::getUserIdFromEmail($finalData['vendor_email']); + } + + if (!empty($finalData['reassign_email'])) { + $reassignId = self::getUserIdFromEmail($finalData['reassign_email']); + } else { + $reassignId = null; + } + + if (empty($vendorId)) { + return ['success' => false, 'message' => __('Vendor not found!', 'bit-integrations'), 'code' => 400]; + } + + $vendor = dokan()->vendor->delete($vendorId, $reassignId); + + if (!empty($vendor) && isset($vendor['id'])) { + if ($vendor['id'] === 0) { + return ['success' => false, 'message' => __('Vendor not found!', 'bit-integrations'), 'code' => 400]; + } + + $id = $vendor['id']; + $vendorEmail = isset($vendor['email']) ? ' Email: ' . $vendor['email'] : ''; + + // translators: 1: Vendor ID, 2: Vendor email + return ['success' => true, 'message' => wp_sprintf(__('Vendor deleted successfully. (ID: %1$s %2$s)', 'bit-integrations'), $id, $vendorEmail)]; + } + + return ['success' => false, 'message' => __('Something went wrong!', 'bit-integrations'), 'code' => 400]; + } + + public function withdrawRequest($finalData, $selectedVendor, $selectedPaymentMethod) + { + if (empty($selectedVendor) || empty($selectedPaymentMethod) || empty($finalData['amount'])) { + return ['success' => false, 'message' => __('Request parameters are empty!', 'bit-integrations'), 'code' => 400]; + } + + $args = [ + 'method' => $selectedPaymentMethod, + 'user_id' => $selectedVendor, + 'amount' => $finalData['amount'], + 'status' => dokan()->withdraw->get_status_code('pending'), + 'ip' => dokan_get_client_ip(), + 'notes' => isset($finalData['note']) ? $finalData['note'] : '' + ]; + + $hasPendingRequest = dokan()->withdraw->has_pending_request($selectedVendor); + + if ($hasPendingRequest) { + return ['success' => false, 'message' => __('Vendor already have pending withdraw request(s)!', 'bit-integrations'), 'code' => 400]; + } + + $validateRequest = dokan()->withdraw->is_valid_approval_request($args); + + if (is_wp_error($validateRequest)) { + return ['success' => false, 'message' => $validateRequest->get_error_message(), 'code' => $validateRequest->get_error_code()]; + } + + $insertWithdraw = dokan()->withdraw->insert_withdraw($args); + + if ($insertWithdraw) { + // translators: 1: Vendor ID, 2: Amount, 3: Payment method + return ['success' => true, 'message' => wp_sprintf(__('Withdraw request inserted successfully. (Vendor ID: %1$s Amount : %2$s Method: %3$s)', 'bit-integrations'), $args['user_id'], $args['amount'], $args['method'])]; + } + + return ['success' => false, 'message' => __('Something went wrong!', 'bit-integrations'), 'code' => 400]; + } + + public function refundRequest($finalData) + { + if (!is_plugin_active('dokan-pro/dokan-pro.php')) { + return [ + // translators: %s: Plugin name + 'message' => wp_sprintf(__('%s is not installed or activated.', 'bit-integrations'), 'The Dokan Pro'), + 'success' => false, + 'code' => 400 + ]; + } + + if (empty($finalData['order_id']) || empty($finalData['refund_amount'])) { + return ['success' => false, 'message' => __('Request parameters are empty!', 'bit-integrations'), 'code' => 400]; + } + + $args = [ + 'order_id' => $finalData['order_id'], + 'refund_amount' => $finalData['refund_amount'], + 'refund_reason' => isset($finalData['refund_reason']) ? $finalData['refund_reason'] : '', + 'item_tax_totals' => [] + ]; + + $validateRefundAmount = Validator::validate_refund_amount($finalData['refund_amount'], $args); + + if (is_wp_error($validateRefundAmount)) { + return ['success' => false, 'message' => $validateRefundAmount->get_error_message(), 'code' => $validateRefundAmount->get_error_code()]; + } + + $refund = dokan_pro()->refund->create($args); + + if (is_wp_error($refund)) { + return ['success' => false, 'message' => $refund->get_error_message(), 'code' => $refund->get_error_code()]; + } + + $refundAmount = $refund->get_refund_amount(); + $orderId = $refund->get_order_id(); + + // translators: 1: Order ID, 2: Refund amount + return ['success' => true, 'message' => wp_sprintf(__('Refund request added successfully. (Order ID: %1$s Refund Amount: %2$s)', 'bit-integrations'), $orderId, $refundAmount)]; + } + + public static function getUserIdFromEmail($email) + { + if (empty($email) || !is_email($email) || !email_exists($email)) { + return false; + } + + $get_user = get_user_by('email', $email); + + return $get_user->ID; + } + + public function generateReqDataFromFieldMap($data, $fieldMap) + { + $dataFinal = []; + foreach ($fieldMap as $value) { + $triggerValue = $value->formField; + $actionValue = $value->dokanField; + if ($triggerValue === 'custom') { + $dataFinal[$actionValue] = Common::replaceFieldWithValue($value->customValue, $data); + } elseif (!\is_null($data[$triggerValue])) { + $dataFinal[$actionValue] = $data[$triggerValue]; + } + } + + return $dataFinal; + } + + public function execute($fieldValues, $fieldMap, $selectedTask, $actions, $selectedVendor, $selectedPaymentMethod) + { + if (isset($fieldMap[0]) && empty($fieldMap[0]->formField)) { + $finalData = []; + } else { + $finalData = $this->generateReqDataFromFieldMap($fieldValues, $fieldMap); + } + + $type = $typeName = ''; + + if ($selectedTask === 'createVendor') { + $response = $this->createVendor($finalData, $actions); + $type = 'Vendor'; + $typeName = 'Create Vendor'; + } elseif ($selectedTask === 'updateVendor') { + $response = $this->updateVendor($finalData, $selectedVendor, $actions); + $type = 'Vendor'; + $typeName = 'Update Vendor'; + } elseif ($selectedTask === 'deleteVendor') { + $response = $this->deleteVendor($finalData, $selectedVendor); + $type = 'Vendor'; + $typeName = 'Delete Vendor'; + } elseif ($selectedTask === 'withdrawRequest') { + $response = $this->withdrawRequest($finalData, $selectedVendor, $selectedPaymentMethod); + $type = 'Withdraw'; + $typeName = 'Withdraw Request'; + } elseif ($selectedTask === 'refundRequest') { + $response = $this->refundRequest($finalData); + $type = 'Refund'; + $typeName = 'Refund Request'; + } + + if ($response['success']) { + $res = ['message' => $response['message']]; + LogHandler::save($this->_integrationID, wp_json_encode(['type' => $type, 'type_name' => $typeName]), 'success', wp_json_encode($res)); + } else { + LogHandler::save($this->_integrationID, wp_json_encode(['type' => $type, 'type_name' => $typeName]), 'error', wp_json_encode($response)); + } + + return $response; + } +} diff --git a/backend/Actions/Dokan/Routes.php b/backend/Actions/Dokan/Routes.php new file mode 100644 index 000000000..c9d963de7 --- /dev/null +++ b/backend/Actions/Dokan/Routes.php @@ -0,0 +1,12 @@ +selectedRemoveTags; if (empty($api_token) || empty($fieldMap) || empty($accountId)) { + // translators: %s: Placeholder value return new WP_Error('REQ_FIELD_EMPTY', wp_sprintf(__('module, fields are required for %s api', 'bit-integrations'), 'Drip')); } diff --git a/backend/Actions/Drip/RecordApiHelper.php b/backend/Actions/Drip/RecordApiHelper.php new file mode 100644 index 000000000..5c135c541 --- /dev/null +++ b/backend/Actions/Drip/RecordApiHelper.php @@ -0,0 +1,110 @@ +_defaultHeader = [ + 'Authorization' => 'Basic ' . base64_encode("{$api_token}:"), + 'Content-Type' => 'application/json' + ]; + + $this->_integrationID = $integId; + } + + public function upsertSubscriber($accountId, $finalData, $selectedStatus, $selectedTags, $selectedRemoveTags) + { + if (empty($accountId)) { + return ['success' => false, 'message' => __('Account id is Required', 'bit-integrations'), 'code' => 400]; + } + + $apiEndpoints = 'https://api.getdrip.com/v2/' . $accountId . '/subscribers'; + + if (empty($finalData['email'])) { + return ['success' => false, 'message' => __('Required field Email is empty', 'bit-integrations'), 'code' => 400]; + } + + $subscriberData = $customFieldsData = []; + $staticFieldsKey = ['email', 'first_name', 'last_name', 'address1', 'address2', 'city', 'state', 'zip', 'country', 'phone', 'time_zone', 'ip_address']; + + foreach ($finalData as $key => $value) { + if (\in_array($key, $staticFieldsKey)) { + $subscriberData[$key] = $value; + } else { + $customFieldsData[$key] = $value; + } + } + + if (!empty($customFieldsData)) { + $subscriberData['custom_fields'] = (object) $customFieldsData; + } + + if (!empty($selectedStatus)) { + $subscriberData['status'] = $selectedStatus; + } + + if (!empty($selectedTags)) { + $subscriberData['tags'] = explode(',', $selectedTags); + } + + if (!empty($selectedRemoveTags)) { + $subscriberData['remove_tags'] = explode(',', $selectedRemoveTags); + } + + $requestParams = (object) [ + 'subscribers' => [ + (object) $subscriberData + ] + ]; + + return HttpHelper::post($apiEndpoints, wp_json_encode($requestParams), $this->_defaultHeader); + } + + public function generateReqDataFromFieldMap($data, $fieldMap) + { + $dataFinal = []; + foreach ($fieldMap as $value) { + $triggerValue = $value->formField; + $actionValue = $value->dripField; + if ($triggerValue === 'custom') { + $dataFinal[$actionValue] = Common::replaceFieldWithValue($value->customValue, $data); + } elseif (!\is_null($data[$triggerValue])) { + $dataFinal[$actionValue] = $data[$triggerValue]; + } + } + + return $dataFinal; + } + + public function execute($fieldValues, $fieldMap, $accountId, $selectedStatus, $selectedTags, $selectedRemoveTags) + { + $finalData = $this->generateReqDataFromFieldMap($fieldValues, $fieldMap); + $apiResponse = $this->upsertSubscriber($accountId, $finalData, $selectedStatus, $selectedTags, $selectedRemoveTags); + + if (isset($apiResponse->subscribers)) { + $res = ['message' => __('Subscriber upserted successfully', 'bit-integrations')]; + LogHandler::save($this->_integrationID, wp_json_encode(['type' => 'subscriber', 'type_name' => 'Subscriber upsert']), 'success', wp_json_encode($res)); + } else { + LogHandler::save($this->_integrationID, wp_json_encode(['type' => 'subscriber', 'type_name' => 'Subscriber upsert']), 'error', wp_json_encode($apiResponse)); + } + + return $apiResponse; + } +} diff --git a/backend/Actions/Drip/Routes.php b/backend/Actions/Drip/Routes.php new file mode 100644 index 000000000..bf0873c29 --- /dev/null +++ b/backend/Actions/Drip/Routes.php @@ -0,0 +1,12 @@ +flow_details->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; diff --git a/backend/Actions/Dropbox/RecordApiHelper.php b/backend/Actions/Dropbox/RecordApiHelper.php new file mode 100644 index 000000000..a5d2c8d44 --- /dev/null +++ b/backend/Actions/Dropbox/RecordApiHelper.php @@ -0,0 +1,101 @@ +token = $token; + } + + public function uploadFile($folder, $filePath) + { + if ($filePath === '') { + return false; + } + + $body = file_get_contents(Common::filePath(trim($filePath))); + + if (!$body) { + return new WP_Error(423, 'Can\'t open file!'); + } + + $apiEndPoint = $this->contentBaseUri . '/2/files/upload'; + $headers = [ + 'Authorization' => 'Bearer ' . $this->token, + 'Content-Type' => 'application/octet-stream', + 'Dropbox-API-Arg' => wp_json_encode(['path' => $folder . '/' . trim(basename($filePath)), 'mode' => 'add', 'autorename' => true, 'mute' => true, 'strict_conflict' => false]), + ]; + + return HttpHelper::post($apiEndPoint, $body, $headers); + } + + public function handleAllFiles($folderWithFile, $actions, $folderKey = null) + { + foreach ($folderWithFile as $folder => $filePath) { + $folder = $folderKey ? $folderKey : $folder; + if ($filePath == '') { + continue; + } + + if (\is_array($filePath)) { + return $this->handleAllFiles($filePath, $actions, $folder); + } + $response = $this->uploadFile($folder, $filePath); + $this->storeInState($response); + $this->deleteFile($filePath, $actions); + } + } + + public function deleteFile($filePath, $actions) + { + if (isset($actions->delete_from_wp) && $actions->delete_from_wp) { + if (file_exists($filePath)) { + wp_delete_file($filePath); + } + } + } + + public function executeRecordApi($integrationId, $fieldValues, $fieldMap, $actions) + { + $folderWithFile = []; + foreach ($fieldMap as $value) { + if (!\is_null($fieldValues[$value->formField])) { + $folderWithFile[$value->dropboxFormField] = $fieldValues[$value->formField]; + } + } + $this->handleAllFiles($folderWithFile, $actions); + + if (\count($this->successApiResponse) > 0) { + LogHandler::save($integrationId, wp_json_encode(['type' => 'dropbox', 'type_name' => 'file_upload']), 'success', __('All Files Uploaded.', 'bit-integrations') . wp_json_encode($this->successApiResponse)); + } + if (\count($this->errorApiResponse) > 0) { + LogHandler::save($integrationId, wp_json_encode(['type' => 'dropbox', 'type_name' => 'file_upload']), 'error', __('Some Files Can\'t Upload.', 'bit-integrations') . wp_json_encode($this->errorApiResponse)); + } + + return true; + } + + protected function storeInState($response) + { + if (isset($response->id)) { + $this->successApiResponse[] = $response; + } else { + $this->errorApiResponse[] = $response; + } + } +} diff --git a/backend/Actions/Dropbox/Routes.php b/backend/Actions/Dropbox/Routes.php new file mode 100644 index 000000000..baf3de7b6 --- /dev/null +++ b/backend/Actions/Dropbox/Routes.php @@ -0,0 +1,11 @@ +_defaultHeader['Content-Type'] = 'application/json'; + $this->_defaultHeader['Authorization'] = "Bearer {$api_key}"; + $this->_integrationID = $integId; + } + + public function createContact($data, $listName, $apiKey) + { + $tmpData = \is_string($data) ? $data : wp_json_encode([(object) $data]); + $header = [ + 'X-ElasticEmail-ApiKey' => $apiKey, + 'Content-Type' => 'application/json' + ]; + + $insertRecordEndpoint = "https://api.elasticemail.com/v4/contacts?{$listName}"; + + return HttpHelper::post($insertRecordEndpoint, $tmpData, $header); + } + + public function generateReqDataFromFieldMap($data, $fieldMap) + { + $dataFinal = []; + + foreach ($fieldMap as $key => $value) { + $triggerValue = $value->formField; + $actionValue = $value->elasticEmailField; + if ($triggerValue === 'custom') { + $dataFinal[$actionValue] = Common::replaceFieldWithValue($value->customValue, $data); + } elseif (!\is_null($data[$triggerValue])) { + $dataFinal[$actionValue] = $data[$triggerValue]; + } + } + + return $dataFinal; + } + + public function execute($integId, $fieldValues, $fieldMap, $integrationDetails) + { + $finalData = $this->generateReqDataFromFieldMap($fieldValues, $fieldMap); + $listName = $integrationDetails->list_id; + $query = ''; + + foreach ($listName as $key => $val) { + $query .= 'listnames=' . $val . '&'; + } + if (\strlen($query)) { + $query = substr($query, 0, -1); + } + $api_key = $integrationDetails->api_key; + $apiResponse = $this->createContact($finalData, $query, $api_key); + + if (!\is_array($apiResponse)) { + LogHandler::save($integId, wp_json_encode(['type' => 'contacts', 'type_name' => 'contact_add']), 'error', wp_json_encode($apiResponse)); + } else { + LogHandler::save($integId, wp_json_encode(['type' => 'contacts', 'type_name' => 'contact_add']), 'success', wp_json_encode($apiResponse)); + } + + return $apiResponse; + } +} diff --git a/backend/Actions/ElasticEmail/Routes.php b/backend/Actions/ElasticEmail/Routes.php new file mode 100644 index 000000000..bda0b5ffa --- /dev/null +++ b/backend/Actions/ElasticEmail/Routes.php @@ -0,0 +1,11 @@ +_integrationDetails = $integrationDetails; + $this->_integrationID = $integId; + $this->_authToken = $this->_integrationDetails->auth_token; + } + + public function addContact($selectedTags, $finalData, $selectedList) + { + $apiEndpoint = 'https://emailoctopus.com/api/1.6/lists/' . $selectedList . '/contacts'; + + if (empty($finalData['EmailAddress'])) { + return ['success' => false, 'message' => __('Required field Email is empty', 'bit-integrations'), 'code' => 400]; + } + + $data = [ + 'api_key' => $this->_authToken + ]; + + foreach ($finalData as $key => $value) { + if ($key == 'EmailAddress') { + $data['email_address'] = $value; + } else { + $fields[$key] = $value; + } + } + + if (!empty($fields)) { + $data['fields'] = (object) $fields; + } + + if (!empty($selectedTags)) { + $selectedTagsArray = explode(',', $selectedTags); + foreach ($selectedTagsArray as $tag) { + $tags[] = $tag; + } + $data['tags'] = $tags; + } + + if (!empty($this->_integrationDetails->actions->status)) { + $data['status'] = 'UNSUBSCRIBED'; + } else { + $data['status'] = 'SUBSCRIBED'; + } + + $isContactExist = $this->isExist($selectedList, $finalData['EmailAddress']); + + if ($isContactExist && !empty($this->_integrationDetails->actions->update)) { + $tagsUpdate = []; + if (!empty($selectedTags)) { + $selectedTagsUpdate = explode(',', $selectedTags); + foreach ($selectedTagsUpdate as $tag) { + $tagsUpdate[$tag] = true; + } + } + if (!empty($isContactExist->tags)) { + $tagsUpdateRemove = []; + foreach ($isContactExist->tags as $existingTag) { + if (empty($selectedTags)) { + $tagsUpdateRemove[$existingTag] = false; + } else { + if (!\array_key_exists($existingTag, $tagsUpdate)) { + $tagsUpdateRemove[$existingTag] = false; + } + } + } + $tagsUpdate = array_merge($tagsUpdate, $tagsUpdateRemove); + } + if (!empty($tagsUpdate)) { + $data['tags'] = (object) $tagsUpdate; + } + + $apiEndpoint = 'https://emailoctopus.com/api/1.6/lists/' . $selectedList . '/contacts/' . $isContactExist->id; + $this->_requestStoringType = 'updated'; + + return HttpHelper::request($apiEndpoint, 'PUT', $data, null); + } + + $this->_requestStoringType = 'created'; + + return HttpHelper::post($apiEndpoint, $data, null); + } + + public function generateReqDataFromFieldMap($data, $fieldMap) + { + $dataFinal = []; + foreach ($fieldMap as $value) { + $triggerValue = $value->formField; + $actionValue = $value->emailOctopusFormField; + if ($triggerValue === 'custom') { + $dataFinal[$actionValue] = Common::replaceFieldWithValue($value->customValue, $data); + } elseif (!\is_null($data[$triggerValue])) { + $dataFinal[$actionValue] = $data[$triggerValue]; + } + } + + return $dataFinal; + } + + public function execute($selectedTags, $fieldValues, $fieldMap, $selectedList) + { + $finalData = $this->generateReqDataFromFieldMap($fieldValues, $fieldMap); + $apiResponse = $this->addContact($selectedTags, $finalData, $selectedList); + + if ($apiResponse->id) { + $successMessage = ['message' => 'Contact ' . $this->_requestStoringType . ' successfully']; + LogHandler::save($this->_integrationID, wp_json_encode(['type' => 'contact', 'type_name' => 'Contact ' . $this->_requestStoringType]), 'success', wp_json_encode($successMessage)); + } else { + LogHandler::save($this->_integrationID, wp_json_encode(['type' => 'contact', 'type_name' => 'Adding Contact']), 'error', wp_json_encode($apiResponse)); + } + + return $apiResponse; + } + + public function isExist($listId, $EmailAddress) + { + $md5encodedEmail = md5($EmailAddress); + $apiEndpoint = 'https://emailoctopus.com/api/1.6/lists/' . $listId . '/contacts/' . $md5encodedEmail . '?api_key=' . $this->_authToken; + $response = HttpHelper::get($apiEndpoint, null, null); + + if (isset($response->id)) { + return $response; + } + + return false; + } +} diff --git a/backend/Actions/EmailOctopus/Routes.php b/backend/Actions/EmailOctopus/Routes.php new file mode 100644 index 000000000..47f9f169e --- /dev/null +++ b/backend/Actions/EmailOctopus/Routes.php @@ -0,0 +1,12 @@ +_integrationID); diff --git a/backend/Actions/Encharge/RecordApiHelper.php b/backend/Actions/Encharge/RecordApiHelper.php new file mode 100644 index 000000000..3c597bc29 --- /dev/null +++ b/backend/Actions/Encharge/RecordApiHelper.php @@ -0,0 +1,94 @@ +_endpoint = 'https://api.encharge.io/v1/people'; + $this->_defaultHeader['Content-Type'] = 'application/json'; + $this->_defaultHeader['X-Encharge-Token'] = $api_key; + $this->_integrationID = $integId; + } + + /** + * serd data to api + * + * @param mixed $data + * + * @return json response + */ + public function insertRecord($data) + { + return HttpHelper::post($this->_endpoint, $data, $this->_defaultHeader); + } + + public function execute($fieldValues, $fieldMap, $tags) + { + $fieldData = []; + + foreach ($fieldMap as $fieldKey => $fieldPair) { + if (!empty($fieldPair->enChargeFields)) { + // echo $fieldPair->enChargeFields . ' ' . $fieldPair->formField; + if ($fieldPair->formField === 'custom' && isset($fieldPair->customValue)) { + $fieldData[$fieldPair->enChargeFields] = Common::replaceFieldWithValue($fieldPair->customValue, $fieldValues); + } elseif (!\is_null($fieldValues[$fieldPair->formField])) { + $fieldData[$fieldPair->enChargeFields] = $fieldValues[$fieldPair->formField]; + } + } + } + if ($tags !== null) { + $fieldData['tags'] = $this->combineTagsWithExisting($tags, $fieldData['email']); + } + $recordApiResponse = $this->insertRecord(wp_json_encode($fieldData)); + $type = 'insert'; + + if ($recordApiResponse && isset($recordApiResponse->user)) { + $recordApiResponse = [ + 'status' => 'success', + 'email' => $recordApiResponse->user->email + ]; + LogHandler::save($this->_integrationID, ['type' => 'record', 'type_name' => $type], 'success', $recordApiResponse); + } else { + LogHandler::save($this->_integrationID, ['type' => 'record', 'type_name' => $type], 'error', $recordApiResponse); + } + + return $recordApiResponse; + } + + private function combineTagsWithExisting($tags, $email) + { + $endpoint = $this->_endpoint . '?people[0][email]=' . urlencode($email); + + $response = HttpHelper::get($endpoint, null, $this->_defaultHeader); + + if (is_wp_error($response) || empty($response->users[0]->tags ?? '')) { + return $tags; + } + + $existingTags = array_filter(array_map('trim', explode(',', $response->users[0]->tags))); + $newTags = array_filter(array_map('trim', explode(',', $tags))); + + $userTags = array_unique(array_merge($existingTags, $newTags)); + + return implode(',', $userTags); + } +} diff --git a/backend/Actions/Encharge/Routes.php b/backend/Actions/Encharge/Routes.php new file mode 100644 index 000000000..de991c80c --- /dev/null +++ b/backend/Actions/Encharge/Routes.php @@ -0,0 +1,11 @@ +id; $memberId = $integrationDetails->selectedMember ?? null; $lockVersion = $integrationDetails->selectedLockVersion ?? null; - error_log('lockVersion: ' . $lockVersion); $needsMemberLookup = \in_array($actionName, ['update_member', 'delete_member']) && ($actionName === 'delete_member' || empty($memberId)); diff --git a/backend/Actions/Fabman/RecordApiHelper.php b/backend/Actions/Fabman/RecordApiHelper.php new file mode 100644 index 000000000..6ea93e7a1 --- /dev/null +++ b/backend/Actions/Fabman/RecordApiHelper.php @@ -0,0 +1,453 @@ +integrationID = $integrationID; + $this->apiKey = $apiKey; + $this->workspaceId = $workspaceId; + $this->accountId = $accountId; + $this->memberId = $memberId; + $this->lockVersion = $lockVersion; + } + + public function execute($actionName, $fieldValues, $integrationDetails) + { + $finalData = $this->buildBaseData(); + + foreach ($integrationDetails->field_map ?? [] as $fieldMap) { + if (empty($fieldMap->formField) || empty($fieldMap->fabmanFormField)) { + continue; + } + + $rawValue = $this->getFieldValue($fieldMap, $fieldValues); + $validatedValue = $this->validateAndSanitizeField($fieldMap->fabmanFormField, $rawValue); + + if ($validatedValue === false) { + $this->logValidationError($fieldMap->fabmanFormField, $rawValue); + + continue; + } + + $finalData[$fieldMap->fabmanFormField] = $validatedValue; + } + + $apiResponse = $this->executeAction($actionName, $finalData); + $this->logResponse($actionName, $apiResponse); + + return $apiResponse; + } + + private function buildBaseData(): array + { + $data = ['account' => $this->accountId]; + + if ($this->workspaceId) { + $data['space'] = $this->workspaceId; + } + + if ($this->memberId) { + $data['memberId'] = $this->memberId; + } + + return $data; + } + + private function getFieldValue($fieldMap, $fieldValues) + { + if ($fieldMap->formField === 'custom') { + return Common::replaceFieldWithValue($fieldMap->customValue, $fieldValues); + } + + return $fieldValues[$fieldMap->formField] ?? null; + } + + private function logValidationError($fieldName, $value): void + { + LogHandler::save( + $this->integrationID, + ['type' => 'validation', 'field' => $fieldName, 'value' => $value], + 'error', + // translators: %s: Placeholder value + \sprintf(__('Field validation failed for: %s', 'bit-integrations'), $fieldName) + ); + } + + private function executeAction($actionName, $finalData) + { + switch ($actionName) { + case 'create_member': + return $this->createMember($finalData); + case 'update_member': + return $this->updateMember($finalData); + case 'delete_member': + return $this->deleteMember(); + case 'create_spaces': + return $this->createSpace($finalData); + case 'update_spaces': + return $this->updateSpace($finalData); + default: + return new WP_Error('INVALID_ACTION', __('Invalid action name', 'bit-integrations')); + } + } + + private function logResponse($actionName, $apiResponse): void + { + $status = $this->determineResponseStatus($apiResponse); + LogHandler::save( + $this->integrationID, + ['type' => 'record', 'type_name' => $actionName], + $status, + $apiResponse + ); + } + + private function determineResponseStatus($apiResponse): string + { + if (is_wp_error($apiResponse) || (\is_object($apiResponse) && isset($apiResponse->error))) { + return 'error'; + } + + return \in_array(HttpHelper::$responseCode, self::SUCCESS_HTTP_CODES) ? 'success' : 'error'; + } + + private function createMember($data) + { + unset($data['memberId']); + $apiEndpoint = self::API_ENDPOINT . '/members'; + $apiResponse = HttpHelper::post($apiEndpoint, json_encode($data), $this->setHeaders()); + + if (\is_wp_error($apiResponse)) { + return $apiResponse; + } + + if (HttpHelper::$responseCode === 201) { + return $apiResponse; + } + + $errorMessage = isset($apiResponse->error) ? $apiResponse->error : \__('Failed to create member', 'bit-integrations'); + + return new WP_Error('API_ERROR', $errorMessage); + } + + private function updateMember($data) + { + $this->addLockVersionIfValid($data); + + if (empty($this->memberId)) { + return new WP_Error('MISSING_MEMBER_ID', __('The email provided did not match any existing Fabman member.', 'bit-integrations')); + } + + $response = Hooks::apply(Config::withPrefix('fabman_update_member'), false, json_encode($data), $this->setHeaders(), self::API_ENDPOINT, $this->memberId); + + /** + * @deprecated 2.7.8 Use `bit_integrations_fabman_update_member` filter instead. + * @since 2.7.8 + */ + $response = Hooks::apply('btcbi_fabman_update_member', $response, json_encode($data), $this->setHeaders(), self::API_ENDPOINT, $this->memberId); + + return $this->handleFilterResponse($response); + } + + private function deleteMember() + { + if (empty($this->memberId)) { + return new WP_Error('MISSING_MEMBER_ID', __('The email provided did not match any existing Fabman member.', 'bit-integrations')); + } + + $response = Hooks::apply(Config::withPrefix('fabman_delete_member'), false, $this->setHeaders(), self::API_ENDPOINT, $this->memberId); + + /** + * @deprecated 2.7.8 Use `bit_integrations_fabman_delete_member` filter instead. + * @since 2.7.8 + */ + $response = Hooks::apply('btcbi_fabman_delete_member', $response, $this->setHeaders(), self::API_ENDPOINT, $this->memberId); + + return $this->handleFilterResponse($response); + } + + private function createSpace($data) + { + unset($data['space']); + $response = Hooks::apply(Config::withPrefix('fabman_create_space'), false, json_encode($data), $this->setHeaders(), self::API_ENDPOINT); + + /** + * @deprecated 2.7.8 Use `bit_integrations_fabman_create_space` filter instead. + * @since 2.7.8 + */ + $response = Hooks::apply('btcbi_fabman_create_space', $response, json_encode($data), $this->setHeaders(), self::API_ENDPOINT); + + return $this->handleFilterResponse($response); + } + + private function updateSpace($data) + { + if (empty($this->workspaceId)) { + return new WP_Error('MISSING_SPACE_ID', __('Please select a space to update.', 'bit-integrations')); + } + + // lockVersion is required for update operations + if (empty($this->lockVersion) || !is_numeric($this->lockVersion)) { + return new WP_Error('MISSING_LOCK_VERSION', __('Lock version is required for updating space.', 'bit-integrations')); + } + + $data['lockVersion'] = (int) $this->lockVersion; + + $response = Hooks::apply(Config::withPrefix('fabman_update_space'), false, json_encode($data), $this->setHeaders(), self::API_ENDPOINT, $this->workspaceId); + + /** + * @deprecated 2.7.8 Use `bit_integrations_fabman_update_space` filter instead. + * @since 2.7.8 + */ + $response = Hooks::apply('btcbi_fabman_update_space', $response, json_encode($data), $this->setHeaders(), self::API_ENDPOINT, $this->workspaceId); + + return $this->handleFilterResponse($response); + } + + private function addLockVersionIfValid(array &$data): void + { + if ($this->lockVersion === null || $this->lockVersion === '') { + return; + } + + if (is_numeric($this->lockVersion)) { + $data['lockVersion'] = (int) $this->lockVersion; + } + } + + private function handleFilterResponse($response) + { + if (empty($response)) { + // translators: %s: Placeholder value + return (object) ['error' => \wp_sprintf(\__('%s plugin is not installed or activated', 'bit-integrations'), 'Bit Integration Pro')]; + } + + return $response; + } + + /** + * Validates and sanitizes field values based on Fabman API field requirements + * + * @param string $fieldName The Fabman field name + * @param mixed $value The raw value to validate + * + * @return mixed|false The sanitized value or false if validation fails + */ + private function validateAndSanitizeField($fieldName, $value) + { + if ($this->isEmptyValue($value)) { + return $this->isFieldRequired($fieldName) ? false : null; + } + + $value = trim((string) $value); + if ($value === '') { + return $this->isFieldRequired($fieldName) ? false : null; + } + + return $this->validateByFieldType($fieldName, $value); + } + + private function isEmptyValue($value): bool + { + return $value === null || $value === ''; + } + + private function validateByFieldType($fieldName, $value) + { + switch ($fieldName) { + case 'emailAddress': + return $this->validateEmail($value); + case 'firstName': + case 'lastName': + case 'displayName': + return $this->validateName($value); + case 'phone': + return $this->validatePhone($value); + case 'notes': + return $this->validateNotes($value); + case 'lockVersion': + return $this->validateLockVersion($value); + case 'privileges': + return $this->validatePrivileges($value); + case 'memberNumber': + return $this->validateMemberNumber($value); + case 'birthday': + return $this->validateDate($value); + case 'space': + case 'account': + case 'memberId': + return $this->validateId($value); + default: + return $this->sanitizeString($value, self::MAX_STRING_LENGTH); + } + } + + private function isFieldRequired($fieldName): bool + { + return \in_array($fieldName, self::REQUIRED_FIELDS); + } + + private function validateEmail($value) + { + $email = sanitize_email($value); + + return is_email($email) ? $email : false; + } + + private function validateName($value) + { + return $this->sanitizeString($value, self::MAX_NAME_LENGTH); + } + + private function validatePhone($value) + { + $phone = preg_replace('/[^\d\s\+\-\(\)]/', '', $value); + $phone = trim($phone); + + $phoneLength = \strlen($phone); + if ($phoneLength < self::MIN_PHONE_LENGTH || $phoneLength > self::MAX_PHONE_LENGTH) { + return false; + } + + return $phone; + } + + private function validateNotes($value) + { + return $this->sanitizeString($value, self::MAX_NOTES_LENGTH); + } + + private function validateLockVersion($value) + { + if (!is_numeric($value)) { + return false; + } + + $version = (int) $value; + + return $version >= 0 ? $version : false; + } + + private function validatePrivileges($value) + { + if (\is_array($value)) { + return $this->validatePrivilegesArray($value); + } + + return $this->validatePrivilegesScalar($value); + } + + private function validatePrivilegesArray($value) + { + $sanitizedArray = array_map('sanitize_text_field', $value); + + foreach ($sanitizedArray as $privilege) { + if (!\in_array($privilege, self::VALID_PRIVILEGES, true)) { + return false; + } + } + + return $sanitizedArray; + } + + private function validatePrivilegesScalar($value) + { + $sanitizedValue = sanitize_text_field($value); + + return \in_array($sanitizedValue, self::VALID_PRIVILEGES, true) ? $sanitizedValue : false; + } + + private function validateMemberNumber($value) + { + $memberNumber = preg_replace('/[^a-zA-Z0-9\-_]/', '', $value); + + return \strlen($memberNumber) <= self::MAX_MEMBER_NUMBER_LENGTH ? $memberNumber : false; + } + + private function validateDate($value) + { + foreach (self::DATE_FORMATS as $format) { + $dateObj = DateTime::createFromFormat($format, $value); + if ($dateObj !== false) { + return $dateObj->format('c'); + } + } + + return false; + } + + private function validateId($value) + { + if (!is_numeric($value)) { + return false; + } + + $id = (int) $value; + + return $id > 0 ? $id : false; + } + + private function sanitizeString($value, $maxLength = self::MAX_STRING_LENGTH) + { + $sanitized = wp_strip_all_tags($value); + + if (mb_strlen($sanitized) > $maxLength) { + $sanitized = mb_substr($sanitized, 0, $maxLength); + } + + return $sanitized; + } + + private function setHeaders(): array + { + return [ + 'Authorization' => 'Bearer ' . $this->apiKey, + 'Content-Type' => 'application/json' + ]; + } +} diff --git a/backend/Actions/Fabman/Routes.php b/backend/Actions/Fabman/Routes.php new file mode 100644 index 000000000..64d4cbbb9 --- /dev/null +++ b/backend/Actions/Fabman/Routes.php @@ -0,0 +1,11 @@ +company_name; if (empty($fieldMap) || empty($apiKey) || empty($actionName) || empty($comapnyName)) { + // translators: %s: Placeholder value return new WP_Error('REQ_FIELD_EMPTY', wp_sprintf(__('module, fields are required for %s api', 'bit-integrations'), 'Flowlu')); } diff --git a/backend/Actions/Flowlu/RecordApiHelper.php b/backend/Actions/Flowlu/RecordApiHelper.php new file mode 100644 index 000000000..ba5581b2c --- /dev/null +++ b/backend/Actions/Flowlu/RecordApiHelper.php @@ -0,0 +1,164 @@ +integrationDetails = $integrationDetails; + $this->integrationId = $integId; + $this->apiUrl = "https://{$comapnyName}.flowlu.com/api/v1"; + $this->defaultHeader = ['Content-type' => 'application/x-www-form-urlencoded']; + } + + public function addAccount($finalData, $apiKey) + { + if (empty($finalData['name'])) { + return ['success' => false, 'message' => __('Required field Name is empty', 'bit-integrations'), 'code' => 400]; + } elseif (!isset($this->integrationDetails->selectedAccountType) || empty($this->integrationDetails->selectedAccountType)) { + return ['success' => false, 'message' => __('Required field Account type is empty', 'bit-integrations'), 'code' => 400]; + } + + $finalData['type'] = $this->integrationDetails->selectedAccountType; + if (isset($this->integrationDetails->selectedCategory) && !empty($this->integrationDetails->selectedCategory)) { + $finalData['account_category_id'] = ($this->integrationDetails->selectedCategory); + } + if (isset($this->integrationDetails->selectedIndustry) && !empty($this->integrationDetails->selectedIndustry)) { + $finalData['industry_id'] = ($this->integrationDetails->selectedIndustry); + } + + $this->type = 'Account'; + $this->typeName = 'Account created'; + $apiEndpoint = $this->apiUrl . "/module/crm/account/create?api_key={$apiKey}"; + + return HttpHelper::post($apiEndpoint, $finalData, $this->defaultHeader); + } + + public function addOpportunity($finalData, $apiKey) + { + if (empty($finalData['name'])) { + return ['success' => false, 'message' => __('Required field lastName is empty', 'bit-integrations'), 'code' => 400]; + } elseif (!isset($this->integrationDetails->selectedPipeline) || empty($this->integrationDetails->selectedPipeline)) { + return ['success' => false, 'message' => __('Required field Pipeline is empty', 'bit-integrations'), 'code' => 400]; + } elseif (!isset($this->integrationDetails->selectedOpportunityStage) || empty($this->integrationDetails->selectedOpportunityStage)) { + return ['success' => false, 'message' => __('Required field Opportunity Stage is empty', 'bit-integrations'), 'code' => 400]; + } + + $finalData['pipeline_id'] = $this->integrationDetails->selectedPipeline; + $finalData['pipeline_stage_id'] = $this->integrationDetails->selectedOpportunityStage; + + if (isset($this->integrationDetails->selectedSource) && !empty($this->integrationDetails->selectedSource)) { + $finalData['source_id'] = ($this->integrationDetails->selectedSource); + } + if (isset($this->integrationDetails->selectedCustomer) && !empty($this->integrationDetails->selectedCustomer)) { + $finalData['customer_id'] = ($this->integrationDetails->selectedCustomer); + } + + $this->type = 'Opportunity'; + $this->typeName = 'Opportunity created'; + $apiEndpoint = $this->apiUrl . "/module/crm/lead/create?api_key={$apiKey}"; + + return HttpHelper::post($apiEndpoint, $finalData, $this->defaultHeader); + } + + public function addProject($finalData, $apiKey) + { + if (empty($finalData['name'])) { + return ['success' => false, 'message' => __('Required field lastName is empty', 'bit-integrations'), 'code' => 400]; + } + + if (isset($this->integrationDetails->selectedManager) && !empty($this->integrationDetails->selectedManager)) { + $finalData['manager_id'] = ($this->integrationDetails->selectedManager); + } + if (isset($this->integrationDetails->selectedProjectStage) && !empty($this->integrationDetails->selectedProjectStage)) { + $finalData['stage_id'] = ($this->integrationDetails->selectedProjectStage); + } + if (isset($this->integrationDetails->selectedPortfolio) && !empty($this->integrationDetails->selectedPortfolio)) { + $finalData['briefcase_id'] = ($this->integrationDetails->selectedPortfolio); + } + if (isset($this->integrationDetails->selectedPriority) && !empty($this->integrationDetails->selectedPriority)) { + $finalData['priority'] = ($this->integrationDetails->selectedPriority); + } + if (isset($this->integrationDetails->selectedProjectOpportunity) && !empty($this->integrationDetails->selectedProjectOpportunity)) { + $finalData['crm_lead_id'] = ($this->integrationDetails->selectedProjectOpportunity); + } + if (isset($this->integrationDetails->selectedCustomer) && !empty($this->integrationDetails->selectedCustomer)) { + $finalData['customer_id'] = ($this->integrationDetails->selectedCustomer); + } + + $this->type = 'Project'; + $this->typeName = 'Project created'; + $apiEndpoint = $this->apiUrl . "/module/st/projects/create?api_key={$apiKey}"; + + return HttpHelper::post($apiEndpoint, $finalData, $this->defaultHeader); + } + + public function generateReqDataFromFieldMap($data, $fieldMap) + { + $dataFinal = []; + foreach ($fieldMap as $value) { + $triggerValue = $value->formField; + $actionValue = $value->flowluFormField; + + $dataFinal[$actionValue] = ($triggerValue === 'custom' && !empty($value->customValue)) + ? Common::replaceFieldWithValue($value->customValue, $data) + : $data[$triggerValue]; + } + + return $dataFinal; + } + + public function execute($fieldValues, $fieldMap, $actionName, $apiKey) + { + $finalData = $this->generateReqDataFromFieldMap($fieldValues, $fieldMap); + switch ($actionName) { + case 'account': + $apiResponse = $this->addAccount($finalData, $apiKey); + + break; + case 'opportunity': + $apiResponse = $this->addOpportunity($finalData, $apiKey); + + break; + case 'project': + $apiResponse = $this->addProject($finalData, $apiKey); + + break; + default: + break; + } + + if (!isset($apiResponse->error)) { + $res = [$this->typeName . ' successfully']; + LogHandler::save($this->integrationId, wp_json_encode(['type' => $this->type, 'type_name' => $this->typeName]), 'success', wp_json_encode($res)); + } else { + LogHandler::save($this->integrationId, wp_json_encode(['type' => $this->type, 'type_name' => $this->type . ' creating']), 'error', wp_json_encode($apiResponse)); + } + + return $apiResponse; + } +} diff --git a/backend/Actions/Flowlu/Routes.php b/backend/Actions/Flowlu/Routes.php new file mode 100644 index 000000000..67a493e64 --- /dev/null +++ b/backend/Actions/Flowlu/Routes.php @@ -0,0 +1,21 @@ +_integrationDetails = $integrationDetails; + $this->_integrationID = $integId; + } + + /** + * Execute the integration + * + * @param array $fieldValues Field values from form + * @param array $fieldMap Field mapping + * @param array $utilities Actions to perform + * + * @return array + */ + public function execute($fieldValues, $fieldMap, $utilities) + { + if (!\defined('FLUENTCART_PLUGIN_PATH')) { + return [ + 'success' => false, + 'message' => __('FluentCart is not installed or activated', 'bit-integrations') + ]; + } + + $fieldData = static::generateReqDataFromFieldMap($fieldMap, $fieldValues); + + $mainAction = $this->_integrationDetails->mainAction ?? 'create_order'; + + $defaultResponse = [ + 'success' => false, + // translators: %s: Plugin name + 'message' => wp_sprintf(__('%s plugin is not installed or activate', 'bit-integrations'), 'Bit Integrations Pro') + ]; + + // Route to appropriate action method + switch ($mainAction) { + case 'create_order': + $response = Hooks::apply(Config::withPrefix('fluentcart_create_order'), $defaultResponse, $fieldData, $utilities, $this->_integrationDetails); + + /** + * @deprecated 2.7.8 Use `bit_integrations_fluentcart_create_order` filter instead. + * @since 2.7.8 + */ + $response = Hooks::apply('btcbi_fluentcart_create_order', $response, $fieldData, $utilities, $this->_integrationDetails); + $type = 'order'; + $actionType = 'create_order'; + + break; + + case 'delete_order': + $response = Hooks::apply(Config::withPrefix('fluentcart_delete_order'), $defaultResponse, $fieldData); + + /** + * @deprecated 2.7.8 Use `bit_integrations_fluentcart_delete_order` filter instead. + * @since 2.7.8 + */ + $response = Hooks::apply('btcbi_fluentcart_delete_order', $response, $fieldData); + $type = 'order'; + $actionType = 'delete_order'; + + break; + + case 'update_order_status': + $response = Hooks::apply(Config::withPrefix('fluentcart_update_order_status'), $defaultResponse, $fieldData); + + /** + * @deprecated 2.7.8 Use `bit_integrations_fluentcart_update_order_status` filter instead. + * @since 2.7.8 + */ + $response = Hooks::apply('btcbi_fluentcart_update_order_status', $response, $fieldData); + $type = 'order'; + $actionType = 'update_order_status'; + + break; + + case 'update_payment_status': + $response = Hooks::apply(Config::withPrefix('fluentcart_update_payment_status'), $defaultResponse, $fieldData); + + /** + * @deprecated 2.7.8 Use `bit_integrations_fluentcart_update_payment_status` filter instead. + * @since 2.7.8 + */ + $response = Hooks::apply('btcbi_fluentcart_update_payment_status', $response, $fieldData); + $type = 'order'; + $actionType = 'update_payment_status'; + + break; + + case 'update_shipping_status': + $response = Hooks::apply(Config::withPrefix('fluentcart_update_shipping_status'), $defaultResponse, $fieldData); + + /** + * @deprecated 2.7.8 Use `bit_integrations_fluentcart_update_shipping_status` filter instead. + * @since 2.7.8 + */ + $response = Hooks::apply('btcbi_fluentcart_update_shipping_status', $response, $fieldData); + $type = 'order'; + $actionType = 'update_shipping_status'; + + break; + + case 'create_customer': + $response = Hooks::apply(Config::withPrefix('fluentcart_create_customer'), $defaultResponse, $fieldData); + + /** + * @deprecated 2.7.8 Use `bit_integrations_fluentcart_create_customer` filter instead. + * @since 2.7.8 + */ + $response = Hooks::apply('btcbi_fluentcart_create_customer', $response, $fieldData); + $type = 'customer'; + $actionType = 'create_customer'; + + break; + + case 'update_customer': + $response = Hooks::apply(Config::withPrefix('fluentcart_update_customer'), $defaultResponse, $fieldData); + + /** + * @deprecated 2.7.8 Use `bit_integrations_fluentcart_update_customer` filter instead. + * @since 2.7.8 + */ + $response = Hooks::apply('btcbi_fluentcart_update_customer', $response, $fieldData); + $type = 'customer'; + $actionType = 'update_customer'; + + break; + + case 'delete_customer': + $response = Hooks::apply(Config::withPrefix('fluentcart_delete_customer'), $defaultResponse, $fieldData); + + /** + * @deprecated 2.7.8 Use `bit_integrations_fluentcart_delete_customer` filter instead. + * @since 2.7.8 + */ + $response = Hooks::apply('btcbi_fluentcart_delete_customer', $response, $fieldData); + $type = 'customer'; + $actionType = 'delete_customer'; + + break; + + case 'create_product': + $response = Hooks::apply(Config::withPrefix('fluentcart_create_product'), $defaultResponse, $fieldData, $utilities, $this->_integrationDetails); + + /** + * @deprecated 2.7.8 Use `bit_integrations_fluentcart_create_product` filter instead. + * @since 2.7.8 + */ + $response = Hooks::apply('btcbi_fluentcart_create_product', $response, $fieldData, $utilities, $this->_integrationDetails); + $type = 'product'; + $actionType = 'create_product'; + + break; + + case 'delete_product': + $response = Hooks::apply(Config::withPrefix('fluentcart_delete_product'), $defaultResponse, $fieldData); + + /** + * @deprecated 2.7.8 Use `bit_integrations_fluentcart_delete_product` filter instead. + * @since 2.7.8 + */ + $response = Hooks::apply('btcbi_fluentcart_delete_product', $response, $fieldData); + $type = 'product'; + $actionType = 'delete_product'; + + break; + + case 'create_coupon': + $response = Hooks::apply(Config::withPrefix('fluentcart_create_coupon'), $defaultResponse, $fieldData, $this->_integrationDetails); + + /** + * @deprecated 2.7.8 Use `bit_integrations_fluentcart_create_coupon` filter instead. + * @since 2.7.8 + */ + $response = Hooks::apply('btcbi_fluentcart_create_coupon', $response, $fieldData, $this->_integrationDetails); + $type = 'coupon'; + $actionType = 'create_coupon'; + + break; + + case 'delete_coupon': + $response = Hooks::apply(Config::withPrefix('fluentcart_delete_coupon'), $defaultResponse, $fieldData); + + /** + * @deprecated 2.7.8 Use `bit_integrations_fluentcart_delete_coupon` filter instead. + * @since 2.7.8 + */ + $response = Hooks::apply('btcbi_fluentcart_delete_coupon', $response, $fieldData); + $type = 'coupon'; + $actionType = 'delete_coupon'; + + break; + + default: + $response = [ + 'success' => false, + 'message' => __('Invalid action', 'bit-integrations') + ]; + $type = 'FluentCart'; + $actionType = 'unknown'; + + break; + } + + $responseType = isset($response['success']) && $response['success'] ? 'success' : 'error'; + LogHandler::save($this->_integrationID, ['type' => $type, 'type_name' => $actionType], $responseType, $response); + + return $response; + } + + private function generateReqDataFromFieldMap($fieldMap, $fieldValues) + { + $dataFinal = []; + foreach ($fieldMap as $item) { + $triggerValue = $item->formField; + $actionValue = $item->fluentCartField; + + $dataFinal[$actionValue] = $triggerValue === 'custom' && isset($item->customValue) ? Common::replaceFieldWithValue($item->customValue, $fieldValues) : $fieldValues[$triggerValue] ?? ''; + } + + return $dataFinal; + } +} diff --git a/backend/Actions/FluentCart/Routes.php b/backend/Actions/FluentCart/Routes.php new file mode 100644 index 000000000..3dbcf1a05 --- /dev/null +++ b/backend/Actions/FluentCart/Routes.php @@ -0,0 +1,16 @@ +actionName; if (empty($fieldMap)) { + // translators: %s: Placeholder value return new WP_Error('REQ_FIELD_EMPTY', wp_sprintf(__('module, fields are required for %s api', 'bit-integrations'), 'Fluent CRM')); } diff --git a/backend/Actions/FluentCrm/RecordApiHelper.php b/backend/Actions/FluentCrm/RecordApiHelper.php new file mode 100644 index 000000000..a411ccd4a --- /dev/null +++ b/backend/Actions/FluentCrm/RecordApiHelper.php @@ -0,0 +1,188 @@ +_integrationID = $integId; + } + + public function insertRecord($data, $actions) + { + // for exsist user + $subscriber = Subscriber::where('email', $data['email'])->first(); + + if ($subscriber && isset($actions->skip_if_exists) && $actions->skip_if_exists) { + $response = [ + 'success' => false, + 'messages' => __('Contact already exists!', 'bit-integrations') + ]; + } else { + // for subscirber + if (!$subscriber) { + if (isset($actions->double_opt_in) && $actions->double_opt_in) { + $data['status'] = 'pending'; + } else { + $data['status'] = 'subscribed'; + } + + $subscriber = FluentCrmApi('contacts')->createOrUpdate($data, false, false); + + if ($subscriber->status === 'pending') { + $subscriber->sendDoubleOptinEmail(); + } + if ($subscriber) { + $response = [ + 'success' => true, + 'messages' => __('Insert successfully!', 'bit-integrations') + ]; + } else { + $response = [ + 'success' => false, + 'messages' => __('Something wrong!', 'bit-integrations') + ]; + } + } else { + $hasDouBleOptIn = isset($actions->double_opt_in) && $actions->double_opt_in; + + $forceSubscribed = !$hasDouBleOptIn && ($subscriber->status != 'subscribed'); + + if ($forceSubscribed) { + $data['status'] = 'subscribed'; + } + + $subscriber = FluentCrmApi('contacts')->createOrUpdate($data, $forceSubscribed, false); + + if ($hasDouBleOptIn && ($subscriber->status === 'pending' || $subscriber->status === 'unsubscribed')) { + $subscriber->sendDoubleOptinEmail(); + } + if ($subscriber) { + $response = [ + 'success' => true, + 'messages' => __('Insert successfully!', 'bit-integrations') + ]; + } else { + $response = [ + 'success' => false, + 'messages' => __('Something wrong!', 'bit-integrations') + ]; + } + } + } + + return $response; + } + + public function insertDeleteTag($data, $actionName) + { + $subscriber = Subscriber::where('email', $data['email'])->first(); + + if (!$subscriber) { + return ['success' => false, 'messages' => __("Contact doesn't exists!", 'bit-integrations')]; + } + + $tags = $data['tags']; + + if ($actionName === 'add-tag') { + $subscriber->attachTags($tags); + $message = __('Tag added successfully!', 'bit-integrations'); + } else { + $subscriber->detachTags($tags); + $message = __('Tag remove successfully!', 'bit-integrations'); + } + + unset($data['tags']); + FluentCrmApi('contacts')->createOrUpdate($data, false, false); + + return ['success' => true, 'messages' => $message]; + } + + public function removeUser($data) + { + $subscriber = Subscriber::where('email', $data['email'])->first(); + + if (!$subscriber) { + return $response = [ + 'success' => false, + 'messages' => __("Contact doesn't exists!", 'bit-integrations') + ]; + } + + $listId = $data['lists']; + + $subscriber->detachLists($listId); + + return $response = [ + 'success' => true, + 'messages' => __('User remove from list successfully!', 'bit-integrations') + ]; + } + + public function execute($fieldValues, $fieldMap, $actions, $list_id, $tags, $actionName) + { + $fieldData = Hooks::apply(Config::withPrefix('fluent_crm_assign_company'), [], (array) $actions); + + /** + * @deprecated 2.7.8 Use `bit_integrations_fluent_crm_assign_company` filter instead. + * @since 2.7.8 + */ + $fieldData = Hooks::apply('btcbi_fluent_crm_assign_company', $fieldData, (array) $actions); + + foreach ($fieldMap as $fieldKey => $fieldPair) { + if (!empty($fieldPair->fluentCRMField)) { + if ($fieldPair->formField === 'custom' && isset($fieldPair->customValue)) { + $fieldData[$fieldPair->fluentCRMField] = $fieldPair->customValue; + } else { + $fieldData[$fieldPair->fluentCRMField] = $fieldValues[$fieldPair->formField]; + } + } + } + + if (!\is_null($list_id)) { + $fieldData['lists'] = [$list_id]; + } + if (!\is_null($tags)) { + $fieldData['tags'] = $tags; + } + + switch ($actionName) { + case 'add-tag': + case 'remove-tag': + $recordApiResponse = $this->insertDeleteTag($fieldData, $actionName); + + break; + case 'add-user': + $recordApiResponse = $this->insertRecord($fieldData, $actions); + + break; + case 'remove-user': + $recordApiResponse = $this->removeUser($fieldData); + + break; + } + + if ($recordApiResponse['success']) { + LogHandler::save($this->_integrationID, ['type' => 'record', 'type_name' => $actionName], 'success', $recordApiResponse); + } else { + LogHandler::save($this->_integrationID, ['type' => 'record', 'type_name' => $actionName], 'error', $recordApiResponse); + } + + return $recordApiResponse; + } +} diff --git a/backend/Actions/FluentCrm/Routes.php b/backend/Actions/FluentCrm/Routes.php new file mode 100644 index 000000000..a125716dc --- /dev/null +++ b/backend/Actions/FluentCrm/Routes.php @@ -0,0 +1,14 @@ +_integrationID = $integrationId; + } + + public function generateReqDataFromFieldMap($data, $fieldMap) + { + $dataFinal = []; + + foreach ($fieldMap as $key => $value) { + $triggerValue = $value->formField; + $actionValue = $value->fluentSupportFormField; + + $value = $triggerValue === 'custom' && isset($value->customValue) ? Common::replaceFieldWithValue($value->customValue, $data) : $data[$triggerValue] ?? null; + + if (str_starts_with($actionValue, 'cf_')) { + $dataFinal['custom_fields'][$actionValue] = $value; + } elseif (!\is_null($data[$triggerValue])) { + $dataFinal[$actionValue] = $value; + } + } + + return $dataFinal; + } + + public function createCustomer($finalData, $attachments = null) + { + $customer = Customer::maybeCreateCustomer($finalData); + + if (isset($customer->id)) { + $finalData['customer_id'] = $customer->id; + + return $this->createTicketByExistCustomer($finalData, $customer, $attachments); + } + wp_send_json_error( + __( + 'Create Customer Failed!', + 'bit-integrations' + ), + 400 + ); + } + + public function getCustomerExits($customer_email) + { + $customer = Customer::where('email', $customer_email)->first(); + + return isset($customer->id) ? $customer->id : null; + } + + public function createTicketByExistCustomer($finalData, $customer, $attachments = null) + { + if (!isset($finalData['mailbox_id']) || empty($finalData['mailbox_id'])) { + $mailbox = Helper::getDefaultMailBox(); + $finalData['mailbox_id'] = $mailbox->id ?? null; + } + + $ticket = Ticket::create($finalData); + + if (!isset($ticket->id)) { + wp_send_json_error( + __( + 'Create Ticket Failed!', + 'bit-integrations' + ), + 400 + ); + } + + if (isset($finalData['custom_fields']) && \is_array($finalData['custom_fields'])) { + // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- Using Fluent Support's own hook for compatibility + $fields = apply_filters('fluent_support/ticket_custom_fields', []); + + if (!empty($fields)) { + $customFields = []; + $keys = array_keys($fields); + $validData = array_intersect_key($finalData['custom_fields'], array_flip($keys)); + + foreach ($validData as $dataKey => $value) { + if ($fields[$dataKey]['type'] == 'checkbox') { + $customFields[$dataKey] = \is_array($value) ? $value : explode(',', $value); + } else { + $customFields[$dataKey] = $value; + } + } + + $ticket->syncCustomFields($customFields); + } + } + + if (!empty($attachments)) { + static::uploadTicketFiles($finalData, $attachments, $ticket, $finalData['customer_id'], $this->_integrationID); + } + + return $ticket; + } + + public function execute( + $fieldValues, + $fieldMap, + $integrationDetails + ) { + $attachments = null; + $finalData = $this->generateReqDataFromFieldMap($fieldValues, $fieldMap); + $customerExits = $this->getCustomerExits($finalData['email']); + $finalData['client_priority'] = !empty($integrationDetails->actions->client_priority) ? $integrationDetails->actions->client_priority : 'normal'; + $finalData['agent_id'] = $integrationDetails->actions->support_staff; + + if (isset($integrationDetails->actions->business_inbox) && !empty($integrationDetails->actions->business_inbox)) { + $finalData['mailbox_id'] = $integrationDetails->actions->business_inbox; + } + if (isset($integrationDetails->actions->attachment) && !empty($integrationDetails->actions->attachment)) { + $attachments = $fieldValues[$integrationDetails->actions->attachment]; + } + + if ($customerExits) { + $finalData['customer_id'] = $customerExits; + $apiResponse = $this->createTicketByExistCustomer($finalData, $customerExits, $attachments); + } else { + $apiResponse = $this->createCustomer($finalData, $attachments); + } + + if (isset($apiResponse->errors)) { + LogHandler::save($this->_integrationID, ['type' => 'Ticket', 'type_name' => 'add-Ticket'], 'error', $apiResponse); + } else { + LogHandler::save($this->_integrationID, ['type' => 'Ticket', 'type_name' => 'add-Ticket'], 'success', $apiResponse); + } + + return $apiResponse; + } + + private static function uploadTicketFiles($finalData, $attachments, $ticket, $customer, $flowId) + { + Hooks::run(Config::withPrefix('fluent_support_upload_ticket_attachments'), $finalData, $attachments, $ticket, $customer, $flowId); + + /** + * @deprecated 2.7.8 Use `bit_integrations_fluent_support_upload_ticket_attachments` action instead. + * @since 2.7.8 + */ + Hooks::run('btcbi_fluent_support_upload_ticket_attachments', $finalData, $attachments, $ticket, $customer, $flowId); + + // translators: %s: Placeholder value + LogHandler::save($flowId, ['type' => 'Ticket', 'type_name' => 'Upload-Ticket-Attachments'], 'error', wp_sprintf(__('%s plugin is not installed or activate', 'bit-integrations'), 'Bit Integrations Pro')); + } +} diff --git a/backend/Actions/FluentSupport/Routes.php b/backend/Actions/FluentSupport/Routes.php new file mode 100644 index 000000000..4b459e543 --- /dev/null +++ b/backend/Actions/FluentSupport/Routes.php @@ -0,0 +1,13 @@ +_integrationDetails = $integrationDetails; + $this->_integrationID = $integId; + $this->baseUrl = $this->_integrationDetails->bundle_alias; + $this->_defaultHeader = [ + 'Content-Type' => 'application/json', + 'Authorization' => 'Token token=' . $this->_integrationDetails->api_key + ]; + } + + public function insertRecord( + $module, + $finalData + ) { + if ($module === 'contact') { + $finalData['sales_accounts'] = [(object) ['id' => $this->_integrationDetails->moduleData->account_id, 'is_primary' => true]]; + } + + if ($module === 'deal') { + $finalData['contacts_added_list'] = [$this->_integrationDetails->moduleData->contact_id]; + } + + if ($module === 'account') { + $module = 'sales_account'; + } + + if ($module === 'product') { + $apiEndpoints = 'https://' . $this->baseUrl . '/api/cpq/' . $module . 's'; + } else { + $apiEndpoints = 'https://' . $this->baseUrl . '/api/' . $module . 's'; + } + + $actions = $this->_integrationDetails->actions; + $body = wp_json_encode([$module => $finalData]); + + return HttpHelper::post($apiEndpoints, $body, $this->_defaultHeader); + } + + public function upsertRecord($module, $finalData) + { + $response = Hooks::apply(Config::withPrefix('freshsales_upsert_record'), $module, $finalData, $this->_integrationDetails, $this->_defaultHeader, $this->baseUrl); + + /** + * @deprecated 2.7.8 Use `bit_integrations_freshsales_upsert_record` filter instead. + * @since 2.7.8 + */ + $response = Hooks::apply('btcbi_freshsales_upsert_record', $response, $finalData, $this->_integrationDetails, $this->_defaultHeader, $this->baseUrl); + + if (\is_string($response) && $response == $module) { + return $this->insertRecord($module, $finalData); + } + + return $response; + } + + public function generateReqDataFromFieldMap($data, $fieldMap) + { + $dataFinal = []; + $customFields = []; + + foreach ($fieldMap as $key => $value) { + $triggerValue = $value->formField; + $actionValue = $value->freshSalesFormField; + if (strpos($actionValue, 'cf_') === 0) { + $customFields[$actionValue] = $data[$triggerValue]; + } elseif (strpos($actionValue, 'cf_') === 0 && $triggerValue === 'custom') { + $customFields[$actionValue] = Common::replaceFieldWithValue($value->customValue, $data); + } elseif ($triggerValue === 'custom') { + $dataFinal[$actionValue] = Common::replaceFieldWithValue($value->customValue, $data); + } elseif (!\is_null($data[$triggerValue])) { + $dataFinal[$actionValue] = $data[$triggerValue]; + } + } + + if (!empty($customFields)) { + $dataFinal['custom_field'] = $customFields; + } + + return $dataFinal; + } + + public function execute( + $fieldValues, + $fieldMap, + $module, + $actions + ) { + $finalData = $this->generateReqDataFromFieldMap($fieldValues, $fieldMap); + if (isset($actions->upsert) && $actions->upsert && $module != 'Product') { + $apiResponse = $this->upsertRecord($module, $finalData); + } else { + $apiResponse = $this->insertRecord($module, $finalData); + } + + if (isset($apiResponse->errors)) { + LogHandler::save($this->_integrationID, wp_json_encode(['type' => $module, 'type_name' => 'add-' . $module]), 'error', wp_json_encode($apiResponse)); + } else { + LogHandler::save($this->_integrationID, wp_json_encode(['type' => $module, 'type_name' => 'add-' . $module]), 'success', wp_json_encode($apiResponse)); + } + + return $apiResponse; + } +} diff --git a/backend/Actions/FreshSales/Routes.php b/backend/Actions/FreshSales/Routes.php new file mode 100644 index 000000000..c4032c3c0 --- /dev/null +++ b/backend/Actions/FreshSales/Routes.php @@ -0,0 +1,12 @@ +_payloadBoundary = wp_generate_password(24); + $this->_defaultHeader['Content-Type'] = 'multipart/form-data; boundary=' . $this->_payloadBoundary; + } + + /** + * Helps to execute upload files api + * + * @param string $apiEndPoint FreshDesk API base URL + * @param array $data Data to pass to API + * @param mixed $api_key + * + * @return array|bool $uploadResponse FreshDesk API response + */ + public function uploadFiles($apiEndPoint, $data, $api_key) + { + $data['avatar'] = new CURLFile("{$data['avatar']}"); + + return HttpHelper::post( + $apiEndPoint, + $data, + [ + 'Authorization' => base64_encode("{$api_key}"), + 'Content-Type' => 'multipart/form-data', + ] + ); + } +} diff --git a/includes/Actions/Freshdesk/FreshdeskController.php b/backend/Actions/Freshdesk/FreshdeskController.php similarity index 96% rename from includes/Actions/Freshdesk/FreshdeskController.php rename to backend/Actions/Freshdesk/FreshdeskController.php index 268846af5..a720dc03c 100644 --- a/includes/Actions/Freshdesk/FreshdeskController.php +++ b/backend/Actions/Freshdesk/FreshdeskController.php @@ -4,9 +4,10 @@ * freshdesk Integration */ -namespace BitCode\FI\Actions\Freshdesk; +namespace BitApps\Integrations\Actions\Freshdesk; -use BitCode\FI\Core\Util\HttpHelper; +use BitApps\Integrations\Config; +use BitApps\Integrations\Core\Util\HttpHelper; use WP_Error; /** @@ -162,7 +163,7 @@ public function getAllContactFields($tokenRequestParams) foreach ($apiResponse as $value) { if (!\in_array($value->name, $excludingFields)) { $responseData[] = (object) [ - 'key' => $value->default ? $value->name : "btcbi_cf_{$value->name}", + 'key' => $value->default ? $value->name : Config::withPrefix("cf_{$value->name}"), 'label' => $value->label, 'required' => $value->name == 'email' || $value->name == 'name' ? true : $value->required_for_agents ]; @@ -199,6 +200,7 @@ public function execute($integrationData, $fieldValues) || empty($fieldMap) ) { + // 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; diff --git a/backend/Actions/Freshdesk/RecordApiHelper.php b/backend/Actions/Freshdesk/RecordApiHelper.php new file mode 100644 index 000000000..f8d7dde7b --- /dev/null +++ b/backend/Actions/Freshdesk/RecordApiHelper.php @@ -0,0 +1,277 @@ +_api_key = $api_key; + $this->_integrationID = $integrationId; + } + + public function insertTicket($apiEndpoint, $data, $api_key, $fileTicket) + { + if ( + empty($data) + || empty($api_key) + ) { + wp_send_json_error( + __( + 'Requested parameter is empty', + 'bit-integrations' + ), + 400 + ); + } + $header = [ + 'Authorization' => base64_encode("{$api_key}"), + 'Content-Type' => 'application/json' + ]; + + if ($fileTicket) { + $data = $data + ['attachments' => $fileTicket]; + + $sendPhotoApiHelper = new AllFilesApiHelper(); + + return $sendPhotoApiHelper->allUploadFiles($apiEndpoint, $data, $api_key); + } + $data = wp_json_encode($data); + $apiResponse = HttpHelper::post($apiEndpoint, $data, $header); + if (is_wp_error($apiResponse) || !empty($apiResponse->error)) { + wp_send_json_error( + empty($apiResponse->error) ? 'Unknown' : $apiResponse->error, + 400 + ); + } + $apiResponse->generates_on = time(); + + return $apiResponse; + } + + public function generateReqDataFromFieldMap($data, $fieldMap) + { + $dataFinal = []; + foreach ($fieldMap as $key => $value) { + $triggerValue = $value->formField; + $actionValue = $value->freshdeskFormField; + if ($triggerValue === 'custom') { + $dataFinal[$actionValue] = Common::replaceFieldWithValue($value->customValue, $data); + } elseif (!\is_null($data[$triggerValue])) { + $dataFinal[$actionValue] = $data[$triggerValue]; + } + } + + return $dataFinal; + } + + public function generateReqDataFromFieldMapContact($data, $fieldMapContact) + { + $fieldMap = []; + $customFields = []; + + foreach ($fieldMapContact as $key => $field) { + $actionValue = $field->contactFreshdeskFormField; + + if ($field->formField === 'custom' && isset($field->customValue)) { + $triggerValue = Common::replaceFieldWithValue($field->customValue, $data); + } else { + $triggerValue = $field->formField; + } + + if (strpos($actionValue, 'btcbi_cf_') === 0 || strpos($actionValue, (string) Config::withPrefix('cf_')) === 0) { + $fieldName = substr($actionValue, 9); + + $customFields[$fieldName] = $data[$triggerValue]; + } else { + $fieldMap[$actionValue] = $data[$triggerValue]; + } + } + + if (!empty($customFields)) { + $fieldMap['custom_fields'] = $customFields; + } + + return $fieldMap; + } + + public function fetchContact($app_base_domamin, $email, $api_key) + { + $apiEndpoint = $app_base_domamin . '/api/v2/contacts?email=' . $email; + + if ( + empty($app_base_domamin) + || empty($email) || empty($api_key) + ) { + wp_send_json_error( + __( + 'Requested parameter is empty', + 'bit-integrations' + ), + 400 + ); + } + $header = [ + 'Authorization' => base64_encode("{$api_key}"), + 'Content-Type' => 'application/json' + ]; + $apiEndpoint = $app_base_domamin . '/api/v2/contacts?email=' . $email; + + return HttpHelper::get($apiEndpoint, null, $header); + } + + public function insertContact($app_base_domamin, $finalDataContact, $api_key, $avatar) + { + if ( + empty($app_base_domamin) + || empty($finalDataContact) + || empty($api_key) + ) { + wp_send_json_error( + __( + 'Requested parameter is empty', + 'bit-integrations' + ), + 400 + ); + } + + $apiEndpoint = $app_base_domamin . '/api/v2/contacts/'; + + if ($avatar) { + $data = $finalDataContact + ['avatar' => static::getAvatar($avatar)]; + $sendPhotoApiHelper = new FilesApiHelper(); + + return $sendPhotoApiHelper->uploadFiles($apiEndpoint, $data, $api_key); + } + + $header = [ + 'Authorization' => base64_encode("{$api_key}"), + 'Content-Type' => 'application/json' + ]; + + return HttpHelper::post($apiEndpoint, wp_json_encode($finalDataContact), $header); + } + + public function updateContact($app_base_domamin, $finalDataContact, $api_key, $contactId) + { + if ( + empty($app_base_domamin) + || empty($finalDataContact) + || empty($api_key) || empty($contactId) + ) { + wp_send_json_error( + __( + 'Requested parameter is empty', + 'bit-integrations' + ), + 400 + ); + } + $header = [ + 'Authorization' => base64_encode("{$api_key}"), + 'Content-Type' => 'application/json' + ]; + $data = wp_json_encode($finalDataContact); + $apiEndpoint = $app_base_domamin . '/api/v2/contacts/' . $contactId; + + return HttpHelper::request($apiEndpoint, 'PUT', $data, $header); + } + + public function execute( + $apiEndpoint, + $fieldValues, + $fieldMap, + $fieldMapContact, + $integrationDetails, + $app_base_domamin + ) { + $finalData = $this->generateReqDataFromFieldMap($fieldValues, $fieldMap); + $finalData = $finalData + ['status' => json_decode($integrationDetails->status)] + ['priority' => json_decode($integrationDetails->priority)]; + + if (!empty($integrationDetails->selected_ticket_type)) { + $finalData['type'] = $integrationDetails->selected_ticket_type; + } + if (!empty($integrationDetails->selected_ticket_source)) { + $finalData['source'] = (int) $integrationDetails->selected_ticket_source; + } + if (!empty($integrationDetails->selected_ticket_group)) { + $finalData['group_id'] = (int) $integrationDetails->selected_ticket_group; + } + if (!empty($integrationDetails->selected_ticket_product)) { + $finalData['product_id'] = (int) $integrationDetails->selected_ticket_product; + } + if (!empty($integrationDetails->selected_ticket_agent)) { + $finalData['responder_id'] = (int) $integrationDetails->selected_ticket_agent; + } + + if ($integrationDetails->contactShow) { + $finalDataContact = $this->generateReqDataFromFieldMapContact($fieldValues, $fieldMapContact); + $avatarFieldName = $integrationDetails->actions->attachments; + $avatar = $fieldValues[$avatarFieldName]; + $apiResponseFetchContact = $this->fetchContact($app_base_domamin, $finalDataContact['email'], $integrationDetails->api_key); + + if (empty($apiResponseFetchContact)) { + $typeName = 'create-contact'; + $apiResponseContact = $this->insertContact($app_base_domamin, $finalDataContact, $integrationDetails->api_key, $avatar); + } elseif ($integrationDetails->updateContact) { + $typeName = 'update-contact'; + $contactId = $apiResponseFetchContact[0]->id; + $apiResponseContact = $this->updateContact($app_base_domamin, $finalDataContact, $integrationDetails->api_key, $contactId); + } else { + $finalData['requester_id'] = $apiResponseFetchContact[0]->id; + $typeName = 'fetch-contact'; + $apiResponseContact = ['message' => 'Contact already exists']; + } + + $responseType = 'error'; + if (isset($apiResponseContact->id)) { + $finalData['requester_id'] = $apiResponseContact->id; + $responseType = 'success'; + } + + $finalData['requester_id'] = isset($apiResponseContact->id) ? $apiResponseContact->id : ''; + + LogHandler::save($this->_integrationID, wp_json_encode(['type' => 'contact', 'type_name' => $typeName]), $responseType, wp_json_encode($apiResponseContact)); + } + + $attachmentsFieldName = $integrationDetails->actions->file; + $fileTicket = $fieldValues[$attachmentsFieldName]; + $apiResponse = $this->insertTicket($apiEndpoint, $finalData, $integrationDetails->api_key, $fileTicket); + + if (property_exists($apiResponse, 'errors')) { + LogHandler::save($this->_integrationID, wp_json_encode(['type' => 'ticket', 'type_name' => 'create-ticket']), 'error', wp_json_encode($apiResponse)); + } else { + LogHandler::save($this->_integrationID, wp_json_encode(['type' => 'ticket', 'type_name' => 'create-ticket']), 'success', wp_json_encode($apiResponse)); + } + + return $apiResponse; + } + + private static function getAvatar($avatar) + { + foreach ($avatar as $value) { + if (\is_array($value)) { + return static::getAvatar($value); + } + + return $value; + } + } +} diff --git a/backend/Actions/Freshdesk/Routes.php b/backend/Actions/Freshdesk/Routes.php new file mode 100644 index 000000000..f28fdca91 --- /dev/null +++ b/backend/Actions/Freshdesk/Routes.php @@ -0,0 +1,12 @@ +_integrationID = $integrationID; + // } + + public static function pluginActive($option = null) + { + include_once ABSPATH . 'wp-admin/includes/plugin.php'; + if (is_plugin_active('gamipress/gamipress.php')) { + return $option === 'get_name' ? 'gamipress/gamipress.php' : true; + } + + 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 = []; + + $course_query_args = [ + 'post_type' => 'sfwd-courses', + 'post_status' => 'publish', + 'orderby' => 'post_title', + 'order' => 'ASC', + 'posts_per_page' => -1, + ]; + + $courseList = get_posts($course_query_args); + + foreach ($courseList as $key => $val) { + $courses[] = [ + 'course_id' => $val->ID, + 'course_title' => $val->post_title, + ]; + } + + return $courses; + } + + public static function fetchAllRankType() + { + global $wpdb; + $cache_key = Config::withPrefix('gamipress_rank_types'); + $cache_group = Config::VAR_PREFIX; + $rank_types = wp_cache_get($cache_key, $cache_group); + + if (false !== $rank_types) { + return $rank_types; + } + + $posts_table = esc_sql($wpdb->posts); + + // phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.PreparedSQL.NotPrepared + $rank_types = $wpdb->get_results( + 'SELECT ID, post_name, post_title, post_type FROM ' . $posts_table . " where post_type = 'rank-type' AND post_status = 'publish'" + ); + // phpcs:enable + + wp_cache_set($cache_key, $rank_types, $cache_group, 10 * MINUTE_IN_SECONDS); + + return $rank_types; + } + + public static function fetchAllRankBYType($query_params) + { + $selectRankType = $query_params->domainName; + + global $wpdb; + $cache_key = Config::withPrefix('gamipress_ranks_') . md5((string) $selectRankType); + $cache_group = Config::VAR_PREFIX; + $ranks = wp_cache_get($cache_key, $cache_group); + + if (false === $ranks) { + $posts_table = esc_sql($wpdb->posts); + // phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.PreparedSQL.NotPrepared + $ranks = $wpdb->get_results( + $wpdb->prepare('SELECT ID, post_name, post_title, post_type FROM ' . $posts_table . " where post_type like %s AND post_status = 'publish'", $selectRankType) + ); + // phpcs:enable + wp_cache_set($cache_key, $ranks, $cache_group, 10 * MINUTE_IN_SECONDS); + } + + wp_send_json_success($ranks); + } + + public static function fetchAllAchievementType() + { + global $wpdb; + $cache_key = Config::withPrefix('gamipress_achievement_types'); + $cache_group = Config::VAR_PREFIX; + $achievement_types = wp_cache_get($cache_key, $cache_group); + + if (false !== $achievement_types) { + return $achievement_types; + } + + $posts_table = esc_sql($wpdb->posts); + + // phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.PreparedSQL.NotPrepared + $achievement_types = $wpdb->get_results( + 'SELECT ID, post_name, post_title, post_type FROM ' . $posts_table . " WHERE post_type = 'achievement-type' AND post_status = 'publish' ORDER BY post_title ASC" + ); + // phpcs:enable + + wp_cache_set($cache_key, $achievement_types, $cache_group, 10 * MINUTE_IN_SECONDS); + + return $achievement_types; + } + + public static function fetchAllAchievementBYType($query_params) + { + $selectAchievementType = $query_params->achievementType; + + global $wpdb; + $cache_key = Config::withPrefix('gamipress_achievements_') . md5((string) $selectAchievementType); + $cache_group = Config::VAR_PREFIX; + $awards = wp_cache_get($cache_key, $cache_group); + + if (false === $awards) { + $posts_table = esc_sql($wpdb->posts); + // phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.PreparedSQL.NotPrepared + $awards = $wpdb->get_results( + $wpdb->prepare('SELECT ID, post_name, post_title, post_type FROM ' . $posts_table . " where post_type like %s AND post_status = 'publish'", $selectAchievementType) + ); + // phpcs:enable + wp_cache_set($cache_key, $awards, $cache_group, 10 * MINUTE_IN_SECONDS); + } + + array_unshift($awards, ['ID' => 'Any', 'post_name' => 'any_achievement', 'post_title' => 'Any Achievement']); + + wp_send_json_success($awards); + } + + public static function fetchAllPointType() + { + global $wpdb; + $cache_key = Config::withPrefix('gamipress_point_types'); + $cache_group = Config::VAR_PREFIX; + $points = wp_cache_get($cache_key, $cache_group); + + if (false === $points) { + $posts_table = esc_sql($wpdb->posts); + // phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.PreparedSQL.NotPrepared + $points = $wpdb->get_results( + 'SELECT ID, post_name, post_title, post_type FROM ' . $posts_table . " WHERE post_type = 'points-type' AND post_status = 'publish' ORDER BY post_title ASC" + ); + // phpcs:enable + wp_cache_set($cache_key, $points, $cache_group, 10 * MINUTE_IN_SECONDS); + } + + wp_send_json_success($points); + } + + public function execute($integrationData, $fieldValues) + { + $integrationDetails = $integrationData->flow_details; + $integId = $integrationData->id; + $mainAction = $integrationDetails->mainAction; + $fieldMap = $integrationDetails->field_map; + // $defaultDataConf = $integrationDetails->default; + if ( + empty($integId) + || empty($mainAction) + ) { + return new WP_Error('REQ_FIELD_EMPTY', __('module, select action are require for GamiPress api', 'bit-integrations')); + } + $recordApiHelper = new RecordApiHelper($integrationDetails, $integId); + $gamiPressApiResponse = $recordApiHelper->execute( + $mainAction, + $fieldValues, + $integrationDetails, + $integrationData, + $fieldMap + ); + + if (is_wp_error($gamiPressApiResponse)) { + return $gamiPressApiResponse; + } + + return $gamiPressApiResponse; + } +} diff --git a/backend/Actions/GamiPress/RecordApiHelper.php b/backend/Actions/GamiPress/RecordApiHelper.php new file mode 100644 index 000000000..e12408696 --- /dev/null +++ b/backend/Actions/GamiPress/RecordApiHelper.php @@ -0,0 +1,204 @@ +_integrationDetails = $integrationDetails; + self::$integrationID = $integId; + } + + public static function getIntegrationId() + { + return $integrationID = self::$integrationID; + } + + public function generateReqDataFromFieldMap($data, $fieldMap) + { + $dataFinal = []; + + foreach ($fieldMap as $key => $value) { + $triggerValue = $value->formField; + $actionValue = $value->gamiPressFormField; + if ($triggerValue === 'custom') { + $dataFinal[$actionValue] = Common::replaceFieldWithValue($value->customValue, $data); + } elseif (!\is_null($data[$triggerValue])) { + $dataFinal[$actionValue] = $data[$triggerValue]; + } + } + + return $dataFinal; + } + + public static function addRankToUser($selectedRank, $mainAction) + { + $user_id = get_current_user_id(); + if ($mainAction === '1') { + return gamipress_update_user_rank($user_id, (int) $selectedRank); + } + // $user_id = get_current_user_id(); + $rank_types = gamipress_get_rank_types(); + + $rank_id = (int) $selectedRank; + $rank = get_post($rank_id); + + if (! $rank || ! isset($rank_types[$rank->post_type])) { + return; + } + + $user_rank_id = gamipress_get_user_rank_id(absint($user_id), $rank->post_type); + + if (! empty($user_rank_id) && $rank_id == $user_rank_id) { + return gamipress_revoke_rank_to_user(absint($user_id), $user_rank_id, 0, ['admin_id' => absint($user_id)]); + + // if still rank is assigned to user + $user_rank_id = gamipress_get_user_rank_id(absint($user_id), $rank->post_type); + if (! empty($user_rank_id) && $rank_id == $user_rank_id) { + $meta = "_gamipress_{$rank->post_type}_rank"; + + return gamipress_delete_user_meta($user_id, $meta); + } + } else { + return false; + } + } + + public static function addAchievementToUser($achievementId, $mainAction) + { + $user_id = get_current_user_id(); + if ($mainAction === '2') { + if (!empty($achievementId) && !empty($user_id) && is_numeric((int) $achievementId)) { + gamipress_award_achievement_to_user(absint((int) $achievementId), absint($user_id), get_current_user_id()); + + return true; + } + + return false; + } + if (!empty($achievementId) && !empty($user_id) && is_numeric((int) $achievementId)) { + gamipress_revoke_achievement_to_user(absint((int) $achievementId), absint($user_id)); + + return true; + } + + return false; + } + + public static function addPointToUser($pointType, $points, $mainAction) + { + $user_id = get_current_user_id(); + if ($mainAction === '3') { + return gamipress_award_points_to_user(absint($user_id), absint($points), $pointType); + } + $deduct_points = 0; + + $points_type = $pointType; + + $existing_points = gamipress_get_user_points(absint($user_id), $points_type); + + if (($existing_points - absint($points)) < 0) { + $deduct_points = absint($points) + ($existing_points - absint($points)); + } else { + $deduct_points = absint($points); + } + + return gamipress_deduct_points_to_user(absint($user_id), absint($deduct_points), $points_type); + } + + public function execute( + $mainAction, + $fieldValues, + $integrationDetails, + $integrationData, + $fieldMap + ) { + $fieldData = []; + if ($mainAction === '1') { + $apiResponse = self::addRankToUser($integrationDetails->selectedRank, $mainAction); + if ($apiResponse) { + // translators: %s: Placeholder value + LogHandler::save(self::getIntegrationId(), wp_json_encode(['type' => 'insert', 'type_name' => 'update-rank']), 'success', wp_json_encode(wp_sprintf(__('Added successfully, post id %1$s and post title %2$s', 'bit-integrations'), $apiResponse->ID, $apiResponse->post_title))); + } else { + LogHandler::save(self::getIntegrationId(), wp_json_encode(['type' => 'insert', 'type_name' => 'update-rank']), 'error', wp_json_encode(__('Failed to add rank', 'bit-integrations'))); + } + } + + if ($mainAction === '2') { + $apiResponse = self::addAchievementToUser($integrationDetails->selectedAchievement, $mainAction); + if ($apiResponse) { + LogHandler::save(self::getIntegrationId(), wp_json_encode(['type' => 'insert', 'type_name' => 'update-award']), 'success', wp_json_encode(__('Achievement added successfully', 'bit-integrations'))); + } else { + LogHandler::save(self::getIntegrationId(), wp_json_encode(['type' => 'insert', 'type_name' => 'update-award']), 'error', wp_json_encode(__('Failed to add achievement', 'bit-integrations'))); + } + } + + if ($mainAction === '3') { + $finalData = $this->generateReqDataFromFieldMap($fieldValues, $fieldMap); + $point = (int) $finalData['point']; + if (!empty($point) && is_numeric($point)) { + $apiResponse = self::addPointToUser($integrationDetails->selectedPointType, $point, $mainAction); + if ($apiResponse) { + // translators: %s: Placeholder value + LogHandler::save(self::getIntegrationId(), wp_json_encode(['type' => 'insert', 'type_name' => 'update-point']), 'success', wp_json_encode(wp_sprintf(__('Point added successfully and total points are %s', 'bit-integrations'), $apiResponse))); + } else { + LogHandler::save(self::getIntegrationId(), wp_json_encode(['type' => 'insert', 'type_name' => 'update-point']), 'error', wp_json_encode(__('Failed to add point', 'bit-integrations'))); + } + } else { + LogHandler::save(self::getIntegrationId(), wp_json_encode(['type' => 'insert', 'type_name' => 'update-point']), 'error', wp_json_encode(__('Failed to add point', 'bit-integrations'))); + } + } + + if ($mainAction === '4') { + $apiResponse = self::addRankToUser($integrationDetails->selectedRank, $mainAction); + if ($apiResponse) { + // translators: %s: Placeholder value + LogHandler::save(self::getIntegrationId(), wp_json_encode(['type' => 'revoke', 'type_name' => 'revoke-rank']), 'success', wp_json_encode(wp_sprintf(__('Revoked rank successfully, post id %1$s and post title %2$s', 'bit-integrations'), $apiResponse->ID, $apiResponse->post_title))); + } else { + LogHandler::save(self::getIntegrationId(), wp_json_encode(['type' => 'revoke', 'type_name' => 'revoke-rank']), 'error', wp_json_encode(__('Failed to revoke rank', 'bit-integrations'))); + } + } + + if ($mainAction === '5') { + $apiResponse = self::addAchievementToUser($integrationDetails->selectedAchievement, $mainAction); + if ($apiResponse) { + LogHandler::save(self::getIntegrationId(), wp_json_encode(['type' => 'revoke', 'type_name' => 'revoke-achievement']), 'success', wp_json_encode(__('Achievement revoked successfully', 'bit-integrations'))); + } else { + LogHandler::save(self::getIntegrationId(), wp_json_encode(['type' => 'revoke', 'type_name' => 'revoke-achievement']), 'error', wp_json_encode(__('Failed to revoke achievement', 'bit-integrations'))); + } + } + + if ($mainAction === '6') { + $finalData = $this->generateReqDataFromFieldMap($fieldValues, $fieldMap); + $point = (int) $finalData['point']; + if (!empty($point) && is_numeric($point)) { + $apiResponse = self::addPointToUser($integrationDetails->selectedPointType, $point, $mainAction); + if ($apiResponse) { + // translators: %s: Placeholder value + LogHandler::save(self::getIntegrationId(), wp_json_encode(['type' => 'insert', 'type_name' => 'update-point']), 'success', wp_json_encode(wp_sprintf(__('Point revoked successfully and total points are %s', 'bit-integrations'), $apiResponse))); + } else { + LogHandler::save(self::getIntegrationId(), wp_json_encode(['type' => 'insert', 'type_name' => 'update-point']), 'error', wp_json_encode(__('Failed to revoke point', 'bit-integrations'))); + } + } else { + LogHandler::save(self::getIntegrationId(), wp_json_encode(['type' => 'insert', 'type_name' => 'update-point']), 'error', wp_json_encode(__('Failed operation , point is not valid number', 'bit-integrations'))); + } + } + + return $apiResponse; + } +} diff --git a/backend/Actions/GamiPress/Routes.php b/backend/Actions/GamiPress/Routes.php new file mode 100644 index 000000000..d5bec37ec --- /dev/null +++ b/backend/Actions/GamiPress/Routes.php @@ -0,0 +1,15 @@ +_integrationDetails = $integrationDetails; + $this->_integrationID = $integId; + $this->_defaultHeader = [ + 'X-Auth-Token' => 'api-key ' . $this->_integrationDetails->auth_token + ]; + } + + public function existSubscriber($auth_token, $email) + { + if (empty($auth_token)) { + wp_send_json_error( + __( + 'Requested parameter is empty', + 'bit-integrations' + ), + 400 + ); + } + + $apiEndpoints = $this->baseUrl . "contacts?query[email]={$email}"; + + $response = HttpHelper::get($apiEndpoints, null, $this->_defaultHeader); + + if (empty($response)) { + return false; + } + + return $response; + } + + public function addContactToCampaign($auth_token, $selectedTags, $finalData, $campaign) + { + if (empty($finalData['email'])) { + return ['success' => false, 'message' => __('Required field Email is empty', 'bit-integrations'), 'code' => 400]; + } + + $apiEndpoints = $this->baseUrl . 'contacts'; + $requestParams = [ + 'email' => $finalData['email'], + 'campaign' => $campaign, + ]; + + if (!empty($selectedTags)) { + $splitSelectedTags = explode(',', $selectedTags); + + foreach ($splitSelectedTags as $tag) { + $requestParams['tags'][] = ['tagId' => $tag]; + } + } + + if (isset($this->_integrationDetails->dayOfCycle)) { + $requestParams = Hooks::apply(Config::withPrefix('getresponse_autoresponder_day'), $requestParams, $this->_integrationDetails->dayOfCycle); + + /** + * @deprecated 2.7.8 Use `bit_integrations_getresponse_autoresponder_day` filter instead. + * @since 2.7.8 + */ + $requestParams = Hooks::apply('btcbi_getresponse_autoresponder_day', $requestParams, $this->_integrationDetails->dayOfCycle); + } + + foreach ($finalData as $key => $value) { + if ($key === 'email') { + continue; + } + + if ($key === 'name') { + $requestParams[$key] = $value; + } else { + $requestParams['customFieldValues'][] = (object) [ + 'customFieldId' => $key, + 'value' => (array) $value + ]; + } + } + + $existSubscriber = $this->existSubscriber($auth_token, $finalData['email']); + $shouldUpdate = !empty($existSubscriber[0]->contactId) && !empty($this->_integrationDetails->actions->update); + + if ($shouldUpdate) { + $contactId = $existSubscriber[0]->contactId; + $apiEndpoints = $this->baseUrl . "contacts/{$contactId}"; + + $requestParams['tags'] = $this->getFormattedTags($contactId, $requestParams['tags']); + + return HttpHelper::post($apiEndpoints, $requestParams, $this->_defaultHeader); + } + + return HttpHelper::post($apiEndpoints, $requestParams, $this->_defaultHeader); + } + + public function generateReqDataFromFieldMap($data, $fieldMap) + { + $dataFinal = []; + + foreach ($fieldMap as $value) { + $triggerValue = $value->formField; + $actionValue = $value->getResponseFormField; + if ($triggerValue === 'custom') { + $dataFinal[$actionValue] = Common::replaceFieldWithValue($value->customValue, $data); + } elseif (!\is_null($data[$triggerValue])) { + $dataFinal[$actionValue] = $data[$triggerValue]; + } + } + + return $dataFinal; + } + + public function execute( + $selectedTag, + $type, + $fieldValues, + $fieldMap, + $auth_token, + $campaign + ) { + $finalData = $this->generateReqDataFromFieldMap($fieldValues, $fieldMap); + $apiResponse = $this->addContactToCampaign($auth_token, $selectedTag, $finalData, $campaign); + + if ($apiResponse->contactId) { + $updatedContactId = $apiResponse->contactId; + $apiResponse = null; + } + if ($apiResponse == null) { + $res = ['message' => $updatedContactId ? 'Contact updated successfully' : 'Contact created successfully']; + LogHandler::save($this->_integrationID, wp_json_encode(['type' => 'contact', 'type_name' => $updatedContactId ? 'update-contact' : 'add-contact']), 'success', wp_json_encode($res)); + } else { + LogHandler::save($this->_integrationID, wp_json_encode(['type' => 'contact', 'type_name' => $apiResponse->code == 1008 ? 'update-contact' : 'add-contact']), 'error', wp_json_encode($apiResponse)); + } + + return $apiResponse; + } + + private function getFormattedTags($contactId, $tags = []) + { + $contactEndpoint = $this->baseUrl . "contacts/{$contactId}"; + $contactResponse = HttpHelper::get($contactEndpoint, null, $this->_defaultHeader); + + if (empty($contactResponse->tags)) { + return $tags; + } + + foreach ($contactResponse->tags as $existingTag) { + $tags[] = ['tagId' => $existingTag->tagId]; + } + + return array_unique($tags, SORT_REGULAR); + } +} diff --git a/backend/Actions/GetResponse/Routes.php b/backend/Actions/GetResponse/Routes.php new file mode 100644 index 000000000..258f2169e --- /dev/null +++ b/backend/Actions/GetResponse/Routes.php @@ -0,0 +1,13 @@ +_defaultHeader['Content-Type'] = 'application/json'; + $this->_defaultHeader['Authorization'] = "Bearer {$api_key}"; + $this->_integrationID = $integId; + } + + public function createContact($data) + { + $data = \is_string($data) ? $data : wp_json_encode((object) $data); + $insertRecordEndpoint = 'https://api.getgist.com/contacts'; + + return HttpHelper::post($insertRecordEndpoint, $data, $this->_defaultHeader); + } + + public function generateReqDataFromFieldMap($data, $fieldMap, $integrationDetails) + { + $dataFinal = []; + + foreach ($fieldMap as $key => $value) { + $triggerValue = $value->formField; + $actionValue = $value->getgistFormField; + if ($triggerValue === 'custom') { + $dataFinal[$actionValue] = Common::replaceFieldWithValue($value->customValue, $data); + } elseif (!\is_null($data[$triggerValue])) { + $dataFinal[$actionValue] = $data[$triggerValue]; + } + } + + if (property_exists($integrationDetails, 'user_type') && property_exists($integrationDetails, 'userId') && $integrationDetails->user_type == 'User') { + $dataFinal['user_id'] = $integrationDetails->userId; + } + + return $dataFinal; + } + + public function execute($integId, $fieldValues, $fieldMap, $integrationDetails) + { + $finalData = $this->generateReqDataFromFieldMap($fieldValues, $fieldMap, $integrationDetails); + $apiResponse = $this->createContact($finalData); + + if (!property_exists($apiResponse, 'contact')) { + LogHandler::save($integId, wp_json_encode(['type' => 'contacts', 'type_name' => 'contact_add']), 'error', wp_json_encode($apiResponse)); + } else { + LogHandler::save($integId, wp_json_encode(['type' => 'contacts', 'type_name' => 'contact_add']), 'success', wp_json_encode($apiResponse)); + } + + return $apiResponse; + } +} diff --git a/backend/Actions/Getgist/Routes.php b/backend/Actions/Getgist/Routes.php new file mode 100644 index 000000000..6b7eeed6b --- /dev/null +++ b/backend/Actions/Getgist/Routes.php @@ -0,0 +1,10 @@ + $value) { + $triggerValue = $value->formField; + $actionValue = $value->giveWpFormField; + if ($triggerValue === 'custom') { + $dataFinal[$actionValue] = Common::replaceFieldWithValue($value->customValue, $data); + } elseif (!\is_null($data[$triggerValue])) { + $dataFinal[$actionValue] = $data[$triggerValue]; + } + } + + return $dataFinal; + } + + public function createGiveWpDonar($finalData) + { + if (empty($finalData['name'])) { + $finalData['name'] = ($finalData['first_name'] ?? '') . ' ' . ($finalData['last_name'] ?? ''); + } + + $metaKeys = [ + '_give_donor_first_name' => 'first_name', + '_give_donor_last_name' => 'last_name', + ]; + + if (!class_exists('Give_Donor') || !\function_exists('Give')) { + return; + } + + $donor = new Give_Donor(); + $donorId = $donor->create($finalData); + + if (is_numeric($donorId)) { + foreach ($metaKeys as $metaKey => $field) { + if (isset($finalData[$field])) { + Give()->donor_meta->update_meta($donorId, $metaKey, $finalData[$field]); + } + } + } + + return $donorId; + } + + public function execute( + $mainAction, + $fieldValues, + $fieldMap, + $integrationDetails, + $integId + ) { + $fieldData = []; + $response = null; + $finalData = $this->generateReqDataFromFieldMap($fieldValues, $fieldMap); + if ($mainAction === '1') { + $response = $this->createGiveWpDonar($finalData); + if (!empty($response)) { + // translators: %s: Placeholder value + LogHandler::save($integId, wp_json_encode(['type' => 'create-donar', 'type_name' => 'create-donar-giveWp']), 'success', wp_json_encode(wp_sprintf(__('Donar crated successfully and id is %s', 'bit-integrations'), $response))); + } else { + LogHandler::save($integId, wp_json_encode(['type' => 'create-donar', 'type_name' => 'create-donar-giveWp']), 'error', wp_json_encode(__('Failed to create donar', 'bit-integrations'))); + } + } + + return $response; + } +} diff --git a/backend/Actions/GiveWp/Routes.php b/backend/Actions/GiveWp/Routes.php new file mode 100644 index 000000000..e03dd9e81 --- /dev/null +++ b/backend/Actions/GiveWp/Routes.php @@ -0,0 +1,10 @@ +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; diff --git a/backend/Actions/GoogleCalendar/RecordApiHelper.php b/backend/Actions/GoogleCalendar/RecordApiHelper.php new file mode 100644 index 000000000..71ac5d766 --- /dev/null +++ b/backend/Actions/GoogleCalendar/RecordApiHelper.php @@ -0,0 +1,127 @@ +token = $token; + $this->timeZone = $timeZone; + $this->calendarId = $calendarId; + } + + public function insertEvent($data) + { + $apiEndpoint = 'https://www.googleapis.com/calendar/v3/calendars/' . $this->calendarId . '/events'; + $headers = [ + 'Content-Type' => 'application/json', + 'Authorization' => 'Bearer ' . $this->token, + ]; + + return HttpHelper::post($apiEndpoint, wp_json_encode($data), $headers); + } + + public function freeSlotCheck($startTime, $endTime) + { + $apiEndpoint = 'https://www.googleapis.com/calendar/v3/freeBusy'; + $headers = [ + 'Content-Type' => 'application/json', + 'Authorization' => 'Bearer ' . $this->token, + ]; + $body = [ + 'timeMin' => $startTime, + 'timeMax' => $endTime, + 'timeZone' => $this->timeZone, + 'items' => [[ + 'id' => $this->calendarId, + ]], + ]; + + return HttpHelper::post($apiEndpoint, wp_json_encode($body), $headers); + } + + public function handleInsert($fieldData, $reminderFieldMap, $actions) + { + $data = []; + $dateType = 'dateTime'; + foreach ($fieldData as $title => $value) { + if ($title === 'start' || $title === 'end') { + $date = new DateTime(Helper::formatToISO8601($value), new DateTimeZone($this->timeZone)); + if (isset($actions->allDayEvent)) { + $data[$title]['date'] = $title === 'end' ? $date->modify('+1 day')->format('Y-m-d') : $date->format('Y-m-d'); + $dateType = 'date'; + } else { + $data[$title]['dateTime'] = Helper::formatToISO8601($value, $this->timeZone); + } + $data[$title]['timeZone'] = $this->timeZone; + + continue; + } + $data[$title] = $value; + } + + if (isset($actions->reminders) && \count($reminderFieldMap) > 0) { + $data['reminders'] = [ + 'useDefault' => false, + 'overrides' => $reminderFieldMap + ]; + } + if (isset($actions->skipIfSlotNotEmpty)) { + $apiResponse = $this->freeSlotCheck($data['start'][$dateType], $data['end'][$dateType]); + + if (is_wp_error($apiResponse) || !empty($apiResponse->error)) { + return $apiResponse; + } + if (empty($apiResponse->calendars->{$this->calendarId}->busy)) { + return $this->insertEvent($data); + } + + return 'No free slot at this time'; + } + + return $this->insertEvent($data); + } + + public function executeRecordApi($integrationId, $fieldValues, $fieldMap, $reminderFieldMap, $actions) + { + $fieldData = []; + foreach ($fieldMap as $value) { + if (!empty($value->googleCalendarFormField)) { + if ($value->formField === 'custom') { + $replaceFieldWithValue = Common::replaceFieldWithValue($value->customValue, $fieldValues); + if (isset($replaceFieldWithValue)) { + $fieldData[$value->googleCalendarFormField] = $replaceFieldWithValue; + } + } else { + $fieldData[$value->googleCalendarFormField] = \is_array($fieldValues[$value->formField]) ? wp_json_encode($fieldValues[$value->formField]) : $fieldValues[$value->formField]; + } + } + } + $reminderFieldMap = [...array_filter($reminderFieldMap, fn ($value) => !empty($value->method) && !empty($value->minutes))]; + + $apiResponse = $this->handleInsert($fieldData, $reminderFieldMap, $actions); + + if (!isset($apiResponse->id)) { + if (isset($apiResponse->error)) { + LogHandler::save($integrationId, wp_json_encode(['type' => 'record', 'type_name' => 'insert']), 'error', $apiResponse); + } + LogHandler::save($integrationId, wp_json_encode(['type' => 'record', 'type_name' => 'insert']), 'error', __('Please check if your have access to insert event in this selected calendar', 'bit-integrations')); + } else { + LogHandler::save($integrationId, wp_json_encode(['type' => 'record', 'type_name' => 'insert']), 'success', $apiResponse); + } + } +} diff --git a/backend/Actions/GoogleCalendar/Routes.php b/backend/Actions/GoogleCalendar/Routes.php new file mode 100644 index 000000000..864c12547 --- /dev/null +++ b/backend/Actions/GoogleCalendar/Routes.php @@ -0,0 +1,11 @@ +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'), 'GoogleContact')); return false; diff --git a/backend/Actions/GoogleContacts/RecordApiHelper.php b/backend/Actions/GoogleContacts/RecordApiHelper.php new file mode 100644 index 000000000..5cbf59d4b --- /dev/null +++ b/backend/Actions/GoogleContacts/RecordApiHelper.php @@ -0,0 +1,207 @@ +token = $token; + } + + public function handleInsertContact($data) + { + $apiEndpoint = 'https://people.googleapis.com/v1/people:createContact'; + $headers = [ + 'Content-Type' => 'application/json', + 'Authorization' => 'Bearer ' . $this->token, + ]; + + $dataNew = [ + 'phoneNumbers' => [ + [ + 'value' => !empty($data['phoneNumber']) ? $data['phoneNumber'] : '', + ] + ], + 'names' => [ + [ + 'givenName' => !empty($data['name']) ? $data['name'] : '', + 'familyName' => !empty($data['lastName']) ? $data['lastName'] : '', + ] + ], + 'addresses' => [ + [ + 'city' => !empty($data['city']) ? $data['city'] : '', + 'country' => !empty($data['country']) ? $data['country'] : '', + ] + ], + 'nicknames' => [ + [ + 'value' => !empty($data['nickname']) ? $data['nickname'] : '' + ] + ], + 'locations' => [ + [ + 'value' => !empty($data['locations']) ? $data['locations'] : '' + ] + ], + 'biographies' => [ + [ + 'value' => !empty($data['biographies']) ? $data['biographies'] : '' + ] + ], + 'emailAddresses' => [ + [ + 'value' => !empty($data['email']) ? $data['email'] : '' + ] + ], + 'occupations' => [ + [ + 'value' => !empty($data['occupation']) ? $data['occupation'] : '' + ] + ], + 'organizations' => [ + [ + 'name' => !empty($data['organization']) ? $data['organization'] : '', + ] + ], + ]; + + return HttpHelper::post($apiEndpoint, wp_json_encode($dataNew), $headers); + } + + public function searchContact($data) + { + $apiEndpoint = 'https://people.googleapis.com/v1/people:searchContacts'; + $headers = [ + 'Content-Type' => 'application/json', + 'Authorization' => 'Bearer ' . $this->token, + ]; + + $data = [ + 'query' => $data['name'], + 'readMask' => 'emailAddresses', + ]; + + return HttpHelper::get($apiEndpoint, $data, $headers); + } + + public function handleUpdateContact($data, $resourceName, $eTag) + { + $apiEndpoint = "https://people.googleapis.com/v1/{$resourceName}:updateContact?updatePersonFields=phoneNumbers,names,emailAddresses"; + $headers = [ + 'Content-Type' => 'application/json', + 'Authorization' => 'Bearer ' . $this->token, + ]; + + $dataNew = [ + 'etag' => $eTag, + 'phoneNumbers' => [ + [ + 'value' => !empty($data['phoneNumber']) ? $data['phoneNumber'] : '', + ] + ], + 'names' => [ + [ + 'givenName' => !empty($data['name']) ? $data['name'] : '', + + ] + ], + 'emailAddresses' => [ + [ + 'value' => !empty($data['email']) ? $data['email'] : '' + ] + ], + ]; + + return HttpHelper::request($apiEndpoint, 'PATCH', wp_json_encode($dataNew), $headers); + } + + public function handleUploadPhoto($imageLocation, $resourceName) + { + $apiEndpoint = "https://people.googleapis.com/v1/{$resourceName}:updateContactPhoto"; + $headers = [ + 'Content-Type' => 'application/json', + 'Authorization' => 'Bearer ' . $this->token, + ]; + + // $dataNew = [ + // "photoBytes" => base64_encode(file_get_contents(json_decode($fieldData['organization'])[0])), + // "personFields" => 'addresses,biographies,emailAddresses,names,phoneNumbers' + // ]; + + $response = wp_remote_get($imageLocation); + if (is_wp_error($response)) { + return $response; + } + $imageData = wp_remote_retrieve_body($response); + + $dataNew = [ + 'photoBytes' => base64_encode($imageData), + 'personFields' => 'addresses,biographies,emailAddresses,names,phoneNumbers' + ]; + + return HttpHelper::request($apiEndpoint, 'PATCH', wp_json_encode($dataNew), $headers); + } + + public function executeRecordApi($integrationId, $fieldValues, $fieldMap, $actions, $mainAction) + { + $fieldData = []; + foreach ($fieldMap as $value) { + if (!empty($value->googleContactsFormField)) { + if ($value->formField === 'custom' && isset($value->customValue) && Common::replaceFieldWithValue($value->customValue, $fieldValues)) { + $fieldData[$value->googleContactsFormField] = Common::replaceFieldWithValue($value->customValue, $fieldValues); + } else { + $fieldData[$value->googleContactsFormField] = \is_array($fieldValues[$value->formField]) ? wp_json_encode($fieldValues[$value->formField]) : $fieldValues[$value->formField]; + } + } + } + + if ($mainAction === '1') { + $contactResponse = $this->handleInsertContact($fieldData); + $imageLocation = $fieldValues[$actions->attachments][0]; + $resourceName = $contactResponse->resourceName; + + if (!empty($imageLocation)) { + $this->handleUploadPhoto($fieldData, $imageLocation, $resourceName); + } + if (!property_exists($contactResponse, 'error')) { + LogHandler::save($integrationId, wp_json_encode(['type' => 'contact', 'type_name' => 'insert']), 'success', wp_json_encode($contactResponse)); + } else { + LogHandler::save($integrationId, wp_json_encode(['type' => 'contact', 'type_name' => 'insert']), 'error', wp_json_encode(__('Fail to add contact', 'bit-integrations'))); + } + + return; + } + + if ($mainAction === '2') { + $searchResponse = $this->searchContact($fieldData); + if (!empty($searchResponse)) { + $resourceName = $searchResponse->results[0]->person->resourceName; + $eTag = $searchResponse->results[0]->person->etag; + if (!empty($resourceName) && !empty($eTag)) { + $updateResponse = $this->handleUpdateContact($fieldData, $resourceName, $eTag); + if (!property_exists($updateResponse, 'error')) { + LogHandler::save($integrationId, wp_json_encode(['type' => 'contact', 'type_name' => 'update']), 'success', wp_json_encode($updateResponse)); + } else { + LogHandler::save($integrationId, wp_json_encode(['type' => 'contact', 'type_name' => 'update']), 'error', wp_json_encode(__('Fail to update contact', 'bit-integrations'))); + } + $imageLocation = $fieldValues[$actions->attachments][0]; + if (!empty($imageLocation)) { + $this->handleUploadPhoto($imageLocation, $resourceName); + } + + return; + } + } else { + LogHandler::save($integrationId, wp_json_encode(['type' => 'contact', 'type_name' => 'update']), 'error', wp_json_encode(__('Contact not found, please check the name', 'bit-integrations'))); + } + } + } +} diff --git a/backend/Actions/GoogleContacts/Routes.php b/backend/Actions/GoogleContacts/Routes.php new file mode 100644 index 000000000..239cfbb9f --- /dev/null +++ b/backend/Actions/GoogleContacts/Routes.php @@ -0,0 +1,10 @@ +flow_details->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; diff --git a/backend/Actions/GoogleDrive/RecordApiHelper.php b/backend/Actions/GoogleDrive/RecordApiHelper.php new file mode 100644 index 000000000..4565e94cf --- /dev/null +++ b/backend/Actions/GoogleDrive/RecordApiHelper.php @@ -0,0 +1,113 @@ +token = $token; + } + + public function uploadFile($folder, $file) + { + if ($file === '') { + return false; + } + + $filePath = Common::filePath($file); + $apiEndpoint = 'https://www.googleapis.com/upload/drive/v3/files?uploadType=multipart'; + $boundary = $this->getBoundary(); + $headers = [ + 'Authorization' => 'Bearer ' . $this->token, + 'Content-Type' => 'multipart/related; boundary="' . $boundary . '"', + ]; + + return HttpHelper::post($apiEndpoint, $this->getBody($folder, $filePath, $boundary), $headers); + } + + public function handleAllFiles($folderWithFile, $actions, $folderKey = null) + { + foreach ($folderWithFile as $folder => $file) { + $folder = $folderKey ? $folderKey : $folder; + if ($file == '') { + continue; + } + + if (\is_array($file)) { + $this->handleAllFiles($file, $actions, $folder); + } else { + $response = $this->uploadFile($folder, $file); + $this->storeInState($response); + $this->deleteFile($file, $actions); + } + } + } + + public function deleteFile($file, $actions) + { + if (isset($actions->delete_from_wp) && $actions->delete_from_wp) { + $filePath = Common::filePath($file); + + if (file_exists($filePath)) { + wp_delete_file($filePath); + } + } + } + + public function executeRecordApi($integrationId, $fieldValues, $fieldMap, $actions) + { + $folderWithFile = []; + foreach ($fieldMap as $value) { + if (!\is_null($fieldValues[$value->formField])) { + $folderWithFile[$value->googleDriveFormField][] = $fieldValues[$value->formField]; + } + } + + $this->handleAllFiles($folderWithFile, $actions); + + if (\count($this->successApiResponse) > 0) { + LogHandler::save($integrationId, wp_json_encode(['type' => 'GoogleDrive', 'type_name' => 'file_upload']), 'success', __('All Files Uploaded.', 'bit-integrations') . wp_json_encode($this->successApiResponse)); + } + if (\count($this->errorApiResponse) > 0) { + LogHandler::save($integrationId, wp_json_encode(['type' => 'GoogleDrive', 'type_name' => 'file_upload']), 'error', __('Some Files Can\'t Upload.', 'bit-integrations') . wp_json_encode($this->errorApiResponse)); + } + } + + protected function getBody($folder, $filePath, $boundary) + { + $body = '--' . $boundary . "\r\n"; + $body .= "Content-Type: application/json; charset=UTF-8\r\n\r\n"; + $body .= '{"name": "' . basename($filePath) . '", "parents": ["' . $folder . '"]}' . "\r\n"; + $body .= '--' . $boundary . "\r\n"; + $body .= "Content-Type: application/octet-stream\r\n\r\n"; + $body .= file_get_contents($filePath) . "\r\n"; + $body .= '--' . $boundary . "--\r\n"; + + return $body; + } + + protected function getBoundary() + { + return 'BITCODE_BI_' . md5(time()); + } + + protected function storeInState($response) + { + if (isset($response->id)) { + $this->successApiResponse[] = $response; + } else { + $this->errorApiResponse[] = $response; + } + } +} diff --git a/backend/Actions/GoogleDrive/Routes.php b/backend/Actions/GoogleDrive/Routes.php new file mode 100644 index 000000000..5d13615a0 --- /dev/null +++ b/backend/Actions/GoogleDrive/Routes.php @@ -0,0 +1,11 @@ +_defaultHeader['Authorization'] = "Bearer {$tokenDetails->access_token}"; + $this->_defaultHeader['Content-Type'] = 'application/json'; + $this->_integrationID = $integId; + } + + public function insertRecord($spreadsheetsId, $worksheetName, $header, $headerRow, $data) + { + $insertRecordEndpoint = "https://sheets.googleapis.com/v4/spreadsheets/{$spreadsheetsId}/values/{$worksheetName}!{$headerRow}:append?valueInputOption=USER_ENTERED"; + + return HttpHelper::post($insertRecordEndpoint, $data, $this->_defaultHeader); + } + + public function updateRecord($spreadsheetId, $worksheetInfo, $data) + { + $updateRecordEndpoing = "https://sheets.googleapis.com/v4/spreadsheets/{$spreadsheetId}/values/{$worksheetInfo}?valueInputOption=USER_ENTERED"; + + return HttpHelper::request($updateRecordEndpoing, 'put', $data, $this->_defaultHeader); + } + + public function formatArrayObject($values) + { + $isMatched = false; + $tmpFields = $values; + foreach ($tmpFields as $key => $value) { + if (\is_array($value) || \is_object($value)) { + $isMatched = true; + + break; + } + } + if ($isMatched) { + return wp_json_encode($values, JSON_UNESCAPED_UNICODE); + } + + return implode(',', $values); + } + + public function execute($spreadsheetId, $worksheetName, $headerRow, $header, $actions, $defaultConf, $fieldValues, $fieldMap) + { + $fieldData = []; + $allHeaders = $defaultConf->headers->{$spreadsheetId}->{$worksheetName}->{$headerRow}; + + foreach ($fieldMap as $fieldKey => $fieldPair) { + if (!empty($fieldPair->googleSheetField)) { + if ($fieldPair->formField === 'custom' && isset($fieldPair->customValue)) { + $fieldData[$fieldPair->googleSheetField] = Common::replaceFieldWithValue($fieldPair->customValue, $fieldValues); + } else { + $fieldData[$fieldPair->googleSheetField] = isset($fieldValues[$fieldPair->formField]) && \is_array($fieldValues[$fieldPair->formField]) ? $this->formatArrayObject($fieldValues[$fieldPair->formField]) : $fieldValues[$fieldPair->formField]; + } + } + } + $values = []; + + foreach ($allHeaders as $googleSheetHeader) { + if (!empty($fieldData[$googleSheetHeader])) { + $values[] = $fieldData[$googleSheetHeader]; + } else { + $values[] = ''; + } + } + + $data = []; + $data['range'] = "{$worksheetName}!{$headerRow}"; + $data['majorDimension'] = "{$header}"; + $data['values'][] = $values; + + $recordApiResponse = $this->insertRecord($spreadsheetId, $worksheetName, $header, $headerRow, wp_json_encode($data)); + $type = 'insert'; + if (isset($recordApiResponse->error)) { + LogHandler::save($this->_integrationID, ['type' => 'record', 'type_name' => $type], 'error', $recordApiResponse); + } else { + LogHandler::save($this->_integrationID, ['type' => 'record', 'type_name' => $type], 'success', $recordApiResponse); + } + + return $recordApiResponse; + } +} diff --git a/backend/Actions/GoogleSheet/Routes.php b/backend/Actions/GoogleSheet/Routes.php new file mode 100644 index 000000000..5eabc8ae2 --- /dev/null +++ b/backend/Actions/GoogleSheet/Routes.php @@ -0,0 +1,13 @@ +actionName; if (empty($fieldMap) || empty($appKey) || empty($actionName) || empty($appSecret)) { + // translators: %s: Placeholder value return new WP_Error('REQ_FIELD_EMPTY', wp_sprintf(__('module, fields are required for %s api', 'bit-integrations'), 'Gravitec')); } diff --git a/backend/Actions/Gravitec/RecordApiHelper.php b/backend/Actions/Gravitec/RecordApiHelper.php new file mode 100644 index 000000000..d454749fa --- /dev/null +++ b/backend/Actions/Gravitec/RecordApiHelper.php @@ -0,0 +1,104 @@ +integrationDetails = $integrationDetails; + $this->integrationId = $integId; + $this->defaultHeader = [ + 'Content-Type' => 'application/json', + 'Authorization' => 'Basic ' . base64_encode("{$appKey}:{$appSecret}") + ]; + } + + public function pushNotification($finalData) + { + $this->type = 'Notification'; + $this->typeName = 'Notification Pushed'; + + if (empty($finalData['message'])) { + return ['success' => false, 'message' => __('Required field Message is empty', 'bit-integrations'), 'code' => 400]; + } + if (empty($finalData['icon'])) { + return ['success' => false, 'message' => __('Required field Icon is empty', 'bit-integrations'), 'code' => 400]; + } + if (empty($finalData['redirect_url'])) { + return ['success' => false, 'message' => __('Required field Refirect URL is empty', 'bit-integrations'), 'code' => 400]; + } + + $requestData = []; + $payloadData = []; + $payload = ['message', 'title', 'icon', 'image', 'redirect_url']; + foreach ($finalData as $key => $value) { + if (array_search($key, $payload) === false) { + $requestData[$key] = $value; + } else { + $payloadData[$key] = $value; + } + } + + if (!empty($this->integrationDetails->selectedButtonTitle) && !empty($this->integrationDetails->selectedButtonURL)) { + $payloadData['buttons '] = [(object) [ + 'title' => $this->integrationDetails->selectedButtonTitle, + 'url' => $this->integrationDetails->selectedButtonURL + ]]; + } + + $requestData['payload'] = (object) $payloadData; + $apiEndpoint = 'https://uapi.gravitec.net/api/v3/push'; + + return HttpHelper::post($apiEndpoint, wp_json_encode($requestData), $this->defaultHeader); + } + + public function generateReqDataFromFieldMap($data, $fieldMap) + { + $dataFinal = []; + foreach ($fieldMap as $value) { + $triggerValue = $value->formField; + $actionValue = $value->gravitecFormField; + $dataFinal[$actionValue] = ($triggerValue === 'custom') ? $value->customValue : $data[$triggerValue]; + } + + return $dataFinal; + } + + public function execute($fieldValues, $fieldMap, $actionName) + { + $finalData = $this->generateReqDataFromFieldMap($fieldValues, $fieldMap); + if ($actionName === 'notification') { + $apiResponse = $this->pushNotification($finalData); + } + + if (!isset($apiResponse->error)) { + $res = [$this->typeName . ' successfully']; + LogHandler::save($this->integrationId, wp_json_encode(['type' => $this->type, 'type_name' => $this->typeName]), 'success', wp_json_encode($res)); + } else { + LogHandler::save($this->integrationId, wp_json_encode(['type' => $this->type, 'type_name' => $this->type . ' creating']), 'error', wp_json_encode($apiResponse)); + } + + return $apiResponse; + } +} diff --git a/backend/Actions/Gravitec/Routes.php b/backend/Actions/Gravitec/Routes.php new file mode 100644 index 000000000..b755c128a --- /dev/null +++ b/backend/Actions/Gravitec/Routes.php @@ -0,0 +1,10 @@ +_integrationDetails = $integrationDetails; + $this->_integrationID = $integId; + } + + public function generateReqDataFromFieldMap($data, $fieldMap) + { + $dataFinal = []; + foreach ($fieldMap as $key => $value) { + if (!empty($value->GroundhoggMapField)) { + $triggerValue = $value->formField; + $actionValue = $value->GroundhoggMapField; + if ($triggerValue === 'custom') { + $dataFinal[$actionValue] = Common::replaceFieldWithValue($value->customValue, $data); + } elseif (isset($data[$triggerValue]) && !\is_null($data[$triggerValue])) { + $dataFinal[$actionValue] = $data[$triggerValue]; + } + } + } + + return $dataFinal; + } + + public function generateMetaDataFromFieldMap($data, $fieldMap) + { + $dataFinal = []; + foreach ($fieldMap as $key => $value) { + $triggerValue = $value->formField; + $actionValue = $value->GroundhoggMetaMapField; + if ($triggerValue === 'custom') { + $dataFinal[$actionValue] = Common::replaceFieldWithValue($value->customMetaFormValue, $data); + } elseif (!\is_null($data[$triggerValue])) { + $dataFinal[$actionValue] = $data[$triggerValue]; + } else { + $dataFinal[$actionValue] = $triggerValue; + } + } + + return $dataFinal; + } + + public static function createContact($finalData, $integrationDetails) + { + if (empty($integrationDetails->token) || empty($integrationDetails->public_key) || empty($integrationDetails->domainName)) { + wp_send_json_error( + __( + 'Request parameter is empty', + 'bit-integrations' + ), + 400 + ); + } + + $authorizationHeader = [ + 'Gh-Token' => $integrationDetails->token, + 'Gh-Public-Key' => $integrationDetails->public_key + ]; + $finalData['optin_status'] = (int) $integrationDetails->optin_status; + $apiEndpoint = $integrationDetails->domainName . '/index.php?rest_route=/gh/v3/contacts'; + + if (isset($finalData['note'])) { + $noteData = [ + 'object_type' => 'contact', + 'type' => 'note', + 'content' => $finalData['note'], + ]; + } + + $response = HttpHelper::post($apiEndpoint, $finalData, $authorizationHeader); + if (isset($noteData)) { + $noteData[0]['object_id'] = $response->contact->ID; + $apiEndpoint = $integrationDetails->domainName . '/index.php?rest_route=/gh/v4/notes/'; + + return $response = HttpHelper::post($apiEndpoint, wp_json_encode(['data' => $noteData]), $authorizationHeader); + } + + return $response; + } + + public static function createTag($diffTags, $integrationDetails) + { + if (empty($integrationDetails->token) || empty($integrationDetails->public_key) || empty($integrationDetails->domainName)) { + wp_send_json_error( + __( + 'Request parameter is empty', + 'bit-integrations' + ), + 400 + ); + } + + $authorizationHeader = [ + 'Gh-Token' => $integrationDetails->token, + 'Gh-Public-Key' => $integrationDetails->public_key + ]; + + $apiEndpoint = $integrationDetails->domainName . '/index.php?rest_route=/gh/v3/tags'; + + return HttpHelper::post($apiEndpoint, $diffTags, $authorizationHeader); + } + + public static function checkExitsTagsOrCreate($integrationDetails, $finalReorganizedTags) + { + $authorizationParams = [ + 'Gh-Token' => $integrationDetails->token, + 'Gh-Public-Key' => $integrationDetails->public_key + ]; + $exitsTags = []; + + $apiEndpoint = $integrationDetails->domainName . '/index.php?rest_route=/gh/v3/tags'; + $apiResponse = HttpHelper::get($apiEndpoint, null, $authorizationParams); + if ($apiResponse->status === 'success') { + $tags = $apiResponse->tags; + foreach ($tags as $tag) { + $exitsTags[] = $tag->tag_name; + } + } else { + return; + } + + $diffTags['tags'] = array_diff($finalReorganizedTags, $exitsTags); + + if ($diffTags) { + self::createTag($diffTags, $integrationDetails); + } + } + + public static function addTagsToExitsUser($addTagsToUser, $integrationDetails, $addTagToEmail) + { + $authorizationParams = [ + 'Gh-Token' => $integrationDetails->token, + 'Gh-Public-Key' => $integrationDetails->public_key + ]; + $prePraperData = [ + 'id_or_email' => $addTagToEmail, + 'tags' => $addTagsToUser, + ]; + $apiEndpoint = $integrationDetails->domainName . '/index.php?rest_route=/gh/v3/contacts/apply_tags'; + + return HttpHelper::request($apiEndpoint, 'PUT', $prePraperData, $authorizationParams); + } + + public function execute( + $fieldValues, + $fieldMap, + $integrationDetails, + $actions + ) { + $mainAction = $integrationDetails->mainAction; + $fieldData = []; + $finalData = $this->generateReqDataFromFieldMap($fieldValues, $fieldMap); + // 1 = create contact with tag + if ($mainAction === '1') { + if ($integrationDetails->showMeta) { + $fieldMapMeta = $integrationDetails->field_map_meta; + $metaData = $this->generateMetaDataFromFieldMap($fieldValues, $fieldMapMeta); + $finalData['meta'] = $metaData; + } + if ($actions->tags) { + $finalReorganizedTags = []; + $tags = explode(',', $actions->tags); + foreach ($tags as $tag) { + // TODO: Smart Tag check + if (isset($fieldValues[$tag])) { + $finalReorganizedTags[] = $fieldValues[$tag]; + } else { + $sanitize = ltrim($tag, 'ground-'); + $finalReorganizedTags[] = $sanitize; + } + } + $finalData['tags'] = $finalReorganizedTags; + $this->checkExitsTagsOrCreate($integrationDetails, $finalReorganizedTags); + } + $apiResponseContact = $this->createContact($finalData, $integrationDetails); + } + // 2 = add tag to contact + if ($mainAction === '2') { + $addTagsToUser = []; + $addTagToEmails = []; + $allSelectedEmails = explode(',', $integrationDetails->emailAddress); + foreach ($allSelectedEmails as $emailAddress) { + // $addTagToEmails[] = $fieldValues[$emailAddress]; + $addTagToEmails[] = $fieldValues[$emailAddress]; + } + + if ($integrationDetails->addTagToUser) { + $tags = explode(',', $integrationDetails->addTagToUser); + foreach ($tags as $tag) { + if (isset($fieldValues[$tag])) { + $addTagsToUser[] = $fieldValues[$tag]; + } else { + $sanitize = ltrim($tag, 'ground-'); + $addTagsToUser[] = $sanitize; + } + } + $finalData['tags'] = $addTagsToUser; + } + $this->checkExitsTagsOrCreate($integrationDetails, $addTagsToUser); + foreach ($addTagToEmails as $addTagToEmail) { + $apiResponse = $this->addTagsToExitsUser($addTagsToUser, $integrationDetails, $addTagToEmail); + if (property_exists($apiResponse, 'code')) { + $apiResponseError[$addTagToEmail] = $apiResponse; + } else { + $apiResponseSuccess[$addTagToEmail] = $apiResponse; + } + } + } + + if ($mainAction === '1') { + if ($apiResponseContact->status === 'success') { + LogHandler::save($this->_integrationID, wp_json_encode(['type' => 'record', 'type_name' => 'add-contact']), 'success', $apiResponseContact); + } else { + LogHandler::save($this->_integrationID, wp_json_encode(['type' => 'contact', 'type_name' => 'add-contact']), 'error', $apiResponseContact); + } + } + if ($mainAction === '2') { + if (!empty($apiResponseSuccess)) { + LogHandler::save($this->_integrationID, wp_json_encode(['type' => 'contact', 'type_name' => 'add-tags-contact']), 'success', $apiResponseSuccess); + } elseif (!empty($apiResponseError)) { + LogHandler::save($this->_integrationID, wp_json_encode(['type' => 'contact', 'type_name' => 'add-tags-contact']), 'error', $apiResponseError); + } + } + + return $apiResponse; + } +} diff --git a/backend/Actions/Groundhogg/Routes.php b/backend/Actions/Groundhogg/Routes.php new file mode 100644 index 000000000..dedef3fd1 --- /dev/null +++ b/backend/Actions/Groundhogg/Routes.php @@ -0,0 +1,11 @@ +integrationID = $integId; + $this->version = $version; + $this->locationId = $locationId; + $this->apiKey = $apiKey; + + $this->defaultHeader = [ + 'Authorization' => 'Bearer ' . $apiKey, + 'Content-Type' => 'application/json' + ]; + + $this->v2DefaultResponse = [ + 'success' => false, + // translators: %s: Plugin name + 'message' => wp_sprintf(__('%s plugin is not installed or activate', 'bit-integrations'), 'Bit Integrations Pro'), + 'code' => 400 + ]; + } + + public function createContact($finalData, $selectedOptions, $actions) + { + if (empty($finalData['email'])) { + return ['success' => false, 'message' => __('Request parameter(s) empty!', 'bit-integrations'), 'code' => 400]; + } + + $apiRequestData = self::formatContactData($finalData, $selectedOptions, $actions, 'createContact'); + + if ($this->version === 'v2' && $this->locationId !== '') { + $response = Hooks::apply(Config::withPrefix('high_level_v2_create_contact'), $this->v2DefaultResponse, $apiRequestData, $this->apiKey, $this->locationId); + + /** + * @deprecated 2.7.8 Use `bit_integrations_high_level_v2_create_contact` filter instead. + * @since 2.7.8 + */ + $response = Hooks::apply('btcbi_high_level_v2_create_contact', $response, $apiRequestData, $this->apiKey, $this->locationId); + + return $response; + } + + $apiEndpoint = $this->baseUrl . 'contacts'; + + $response = HttpHelper::post($apiEndpoint, wp_json_encode($apiRequestData), $this->defaultHeader); + + if (isset($response->contact)) { + return ['success' => true, 'message' => __('Contact created successfully.', 'bit-integrations'), 'response' => $response]; + } + + return ['success' => false, 'message' => __('Failed to create contact!', 'bit-integrations'), 'response' => $response, 'code' => 400]; + } + + public function updateContact($finalData, $selectedOptions, $actions) + { + if (empty($selectedOptions['selectedContact']) && empty($finalData['id'])) { + return ['success' => false, 'message' => __('Contact id not found in request!', 'bit-integrations'), 'code' => 400]; + } + + if (!empty($selectedOptions['selectedContact'])) { + $id = $selectedOptions['selectedContact']; + } else { + $id = $finalData['id']; + } + + $apiRequestData = self::formatContactData($finalData, $selectedOptions, $actions, 'updateContact'); + + if ($this->version === 'v2') { + $response = Hooks::apply(Config::withPrefix('high_level_v2_update_contact'), $this->v2DefaultResponse, $apiRequestData, $this->apiKey, $id); + + /** + * @deprecated 2.7.8 Use `bit_integrations_high_level_v2_update_contact` filter instead. + * @since 2.7.8 + */ + $response = Hooks::apply('btcbi_high_level_v2_update_contact', $response, $apiRequestData, $this->apiKey, $id); + + return $response; + } + + $apiEndpoint = $this->baseUrl . 'contacts/' . $id; + + $response = HttpHelper::put($apiEndpoint, wp_json_encode($apiRequestData), $this->defaultHeader); + + if (isset($response->contact)) { + return ['success' => true, 'message' => __('Contact updated successfully.', 'bit-integrations')]; + } + + return ['success' => false, 'message' => __('Failed to update contact!', 'bit-integrations'), 'response' => $response, 'code' => 400]; + } + + public function createTask($finalData, $selectedOptions, $actions) + { + if (empty($selectedOptions['selectedContact']) && empty($finalData['contactId'])) { + return ['success' => false, 'message' => __('Contact id not found in request!', 'bit-integrations'), 'code' => 400]; + } + + if (empty($finalData['dueDate']) || empty($finalData['title'])) { + return ['success' => false, 'message' => __('Request parameter(s) empty!', 'bit-integrations'), 'code' => 400]; + } + + if (!empty($selectedOptions['selectedContact'])) { + $contactId = $selectedOptions['selectedContact']; + } else { + $contactId = $finalData['contactId']; + } + + $apiRequestData['title'] = $finalData['title']; + $apiRequestData['description'] = !empty($finalData['description']) ? $finalData['description'] : ''; + $apiRequestData['dueDate'] = !empty($finalData['dueDate']) ? $finalData['dueDate'] : ''; + $apiRequestData['assignedTo'] = !empty($selectedOptions['selectedUser']) ? $selectedOptions['selectedUser'] : ''; + $apiRequestData['status'] = !empty($selectedOptions['selectedTaskStatus']) ? $selectedOptions['selectedTaskStatus'] : ''; + + if ($this->version === 'v2') { + $response = Hooks::apply(Config::withPrefix('high_level_v2_create_task'), $this->v2DefaultResponse, $apiRequestData, $this->apiKey, $contactId); + + /** + * @deprecated 2.7.8 Use `bit_integrations_high_level_v2_create_task` filter instead. + * @since 2.7.8 + */ + $response = Hooks::apply('btcbi_high_level_v2_create_task', $response, $apiRequestData, $this->apiKey, $contactId); + + return $response; + } + + $apiEndpoint = $this->baseUrl . 'contacts/' . $contactId . '/tasks'; + + $response = HttpHelper::post($apiEndpoint, wp_json_encode($apiRequestData), $this->defaultHeader); + + if (isset($response->id)) { + return ['success' => true, 'message' => __('Task created successfully.', 'bit-integrations')]; + } + + return ['success' => false, 'message' => __('Failed to create task!', 'bit-integrations'), 'response' => $response, 'code' => 400]; + } + + public function updateTask($finalData, $selectedOptions, $actions) + { + if (empty($selectedOptions['selectedContact']) && empty($finalData['contactId'])) { + return ['success' => false, 'message' => __('Contact id not found in request!', 'bit-integrations'), 'code' => 400]; + } + + if (empty($selectedOptions['updateTaskId']) && empty($finalData['taskId'])) { + return ['success' => false, 'message' => __('Task id not found in request!', 'bit-integrations'), 'code' => 400]; + } + + if (empty($finalData['dueDate']) || empty($finalData['title'])) { + return ['success' => false, 'message' => __('Request parameter(s) empty!', 'bit-integrations'), 'code' => 400]; + } + + if (!empty($selectedOptions['selectedContact'])) { + $contactId = $selectedOptions['selectedContact']; + } else { + $contactId = $finalData['contactId']; + } + + if (!empty($selectedOptions['updateTaskId'])) { + $taskId = $selectedOptions['updateTaskId']; + } else { + $taskId = $finalData['taskId']; + } + + $apiRequestData['title'] = $finalData['title']; + $apiRequestData['description'] = !empty($finalData['description']) ? $finalData['description'] : ''; + $apiRequestData['dueDate'] = !empty($finalData['dueDate']) ? $finalData['dueDate'] : ''; + $apiRequestData['assignedTo'] = !empty($selectedOptions['selectedUser']) ? $selectedOptions['selectedUser'] : ''; + $apiRequestData['status'] = !empty($selectedOptions['selectedTaskStatus']) ? $selectedOptions['selectedTaskStatus'] : ''; + + if ($this->version === 'v2') { + $response = Hooks::apply(Config::withPrefix('high_level_v2_update_task'), $this->v2DefaultResponse, $apiRequestData, $this->apiKey, $contactId, $taskId); + + /** + * @deprecated 2.7.8 Use `bit_integrations_high_level_v2_update_task` filter instead. + * @since 2.7.8 + */ + $response = Hooks::apply('btcbi_high_level_v2_update_task', $response, $apiRequestData, $this->apiKey, $contactId, $taskId); + + return $response; + } + + $apiEndpoint = $this->baseUrl . 'contacts/' . $contactId . '/tasks/' . $taskId; + + $response = HttpHelper::put($apiEndpoint, wp_json_encode($apiRequestData), $this->defaultHeader); + + if (isset($response->id)) { + return ['success' => true, 'message' => __('Task updated successfully.', 'bit-integrations')]; + } + + return ['success' => false, 'message' => __('Failed to update task!', 'bit-integrations'), 'response' => $response, 'code' => 400]; + } + + public function createOpportunity($finalData, $selectedOptions, $actions) + { + if (empty($selectedOptions['selectedPipeline']) || empty($selectedOptions['selectedStage']) || empty($finalData['title'])) { + return ['success' => false, 'message' => __('Request parameter(s) empty!', 'bit-integrations'), 'code' => 400]; + } + + if ($selectedOptions['selectedContact']) { + $contactId = $selectedOptions['selectedContact']; + } else { + $contactId = !empty($finalData['contactId']) ? $finalData['contactId'] : ''; + } + + if (empty($finalData['email']) && empty($finalData['phone']) && empty($contactId)) { + return ['success' => false, 'message' => __('Either a Contact ID, Email, or Phone Number is required!', 'bit-integrations'), 'code' => 400]; + } + + $apiRequestData = self::formatOpportunityData($finalData, $selectedOptions, $actions, $contactId, 'createOpportunity'); + + if ($this->version === 'v2' && $this->locationId !== '') { + $response = Hooks::apply(Config::withPrefix('high_level_v2_create_opportunity'), $this->v2DefaultResponse, $apiRequestData, $this->apiKey, $this->locationId, $selectedOptions['selectedPipeline']); + + /** + * @deprecated 2.7.8 Use `bit_integrations_high_level_v2_create_opportunity` filter instead. + * @since 2.7.8 + */ + $response = Hooks::apply('btcbi_high_level_v2_create_opportunity', $response, $apiRequestData, $this->apiKey, $this->locationId, $selectedOptions['selectedPipeline']); + + return $response; + } + + $apiEndpoint = $this->baseUrl . 'pipelines/' . $selectedOptions['selectedPipeline'] . '/opportunities'; + + $response = HttpHelper::post($apiEndpoint, wp_json_encode($apiRequestData), $this->defaultHeader); + + if (isset($response->id)) { + return ['success' => true, 'message' => __('Opportunity created successfully.', 'bit-integrations')]; + } + + return ['success' => false, 'message' => __('Failed to create opportunity!', 'bit-integrations'), 'response' => $response, 'code' => 400]; + } + + public function updateOpportunity($finalData, $selectedOptions, $actions) + { + if (empty($selectedOptions['selectedPipeline']) || empty($selectedOptions['selectedStage']) || ($this->version === 'v1' && empty($finalData['title']))) { + return ['success' => false, 'message' => __('Request parameter(s) empty!', 'bit-integrations'), 'code' => 400]; + } + + if (empty($selectedOptions['selectedOpportunity']) && empty($finalData['opportunityId'])) { + return ['success' => false, 'message' => __('Opportunity id not found in request!', 'bit-integrations'), 'code' => 400]; + } + + if (!empty($selectedOptions['selectedOpportunity'])) { + $opportunityId = $selectedOptions['selectedOpportunity']; + } else { + $opportunityId = $finalData['opportunityId']; + } + + if ($selectedOptions['selectedContact']) { + $contactId = $selectedOptions['selectedContact']; + } else { + $contactId = !empty($finalData['contactId']) ? $finalData['contactId'] : ''; + } + + if (empty($finalData['email']) && empty($finalData['phone']) && empty($contactId)) { + return ['success' => false, 'message' => __('Either a Contact ID, Email, or Phone Number is required!', 'bit-integrations'), 'code' => 400]; + } + + $apiRequestData = self::formatOpportunityData($finalData, $selectedOptions, $actions, $contactId, 'updateOpportunity'); + + if ($this->version === 'v2' && $this->locationId !== '') { + $response = Hooks::apply(Config::withPrefix('high_level_v2_update_opportunity'), $this->v2DefaultResponse, $apiRequestData, $this->apiKey, $opportunityId, $selectedOptions['selectedPipeline']); + + /** + * @deprecated 2.7.8 Use `bit_integrations_high_level_v2_update_opportunity` filter instead. + * @since 2.7.8 + */ + $response = Hooks::apply('btcbi_high_level_v2_update_opportunity', $response, $apiRequestData, $this->apiKey, $opportunityId, $selectedOptions['selectedPipeline']); + + return $response; + } + + $apiEndpoint = $this->baseUrl . 'pipelines/' . $selectedOptions['selectedPipeline'] . '/opportunities/' . $opportunityId; + + $response = HttpHelper::put($apiEndpoint, wp_json_encode($apiRequestData), $this->defaultHeader); + + if (isset($response->id)) { + return ['success' => true, 'message' => __('Opportunity updated successfully.', 'bit-integrations')]; + } + + return ['success' => false, 'message' => __('Failed to update opportunity!', 'bit-integrations'), 'response' => $response, 'code' => 400]; + } + + public function generateReqDataFromFieldMap($data, $fieldMap) + { + $dataFinal = []; + foreach ($fieldMap as $value) { + $triggerValue = $value->formField; + $actionValue = $value->highLevelField; + if ($triggerValue === 'custom') { + $dataFinal[$actionValue] = Common::replaceFieldWithValue($value->customValue, $data); + } elseif (!\is_null($data[$triggerValue])) { + $dataFinal[$actionValue] = $data[$triggerValue]; + } + } + + return $dataFinal; + } + + public function execute($fieldValues, $fieldMap, $selectedTask, $selectedOptions, $actions) + { + if (isset($fieldMap[0]) && empty($fieldMap[0]->formField)) { + $finalData = []; + } else { + $finalData = $this->generateReqDataFromFieldMap($fieldValues, $fieldMap); + } + + $type = $typeName = ''; + + if ($selectedTask === 'createContact') { + $response = $this->createContact($finalData, $selectedOptions, $actions); + $type = 'Contact'; + $typeName = 'Create Contact'; + } elseif ($selectedTask === 'updateContact') { + $response = $this->updateContact($finalData, $selectedOptions, $actions); + $type = 'Contact'; + $typeName = 'Update Contact'; + } elseif ($selectedTask === 'createTask') { + $response = $this->createTask($finalData, $selectedOptions, $actions); + $type = 'Task'; + $typeName = 'Create Task'; + } elseif ($selectedTask === 'updateTask') { + $response = $this->updateTask($finalData, $selectedOptions, $actions); + $type = 'Task'; + $typeName = 'Update Task'; + } elseif ($selectedTask === 'createOpportunity') { + $response = $this->createOpportunity($finalData, $selectedOptions, $actions); + $type = 'Opportunity'; + $typeName = 'Create Opportunity'; + } elseif ($selectedTask === 'updateOpportunity') { + $response = $this->updateOpportunity($finalData, $selectedOptions, $actions); + $type = 'Opportunity'; + $typeName = 'Update Opportunity'; + } + + if ($response['success']) { + LogHandler::save($this->integrationID, wp_json_encode(['type' => $type, 'type_name' => $typeName]), 'success', wp_json_encode($response)); + } else { + LogHandler::save($this->integrationID, wp_json_encode(['type' => $type, 'type_name' => $typeName]), 'error', wp_json_encode($response)); + } + + return $response; + } + + private static function formatContactData($finalData, $selectedOptions, $actions, $module = 'contact') + { + $staticFieldsKey = ['email', 'firstName', 'lastName', 'name', 'phone', 'dateOfBirth', 'address1', 'city', 'state', 'country', 'postalCode', 'companyName', 'website']; + $apiRequestData = $customFieldsData = []; + + foreach ($finalData as $key => $value) { + if (\in_array($key, $staticFieldsKey)) { + $apiRequestData[$key] = $value; + } else { + $keyFieldType = explode('_bihl_', $key); + $fieldKey = $keyFieldType[0]; + $fieldType = $keyFieldType[1]; + + if ($fieldType === 'MULTIPLE_OPTIONS' || $fieldType === 'CHECKBOX') { + $customFieldsData[$fieldKey] = \is_string($value) ? explode(',', str_replace(' ', '', $value)) : $value; + } else { + $customFieldsData[$fieldKey] = $value; + } + } + } + + if (!empty($customFieldsData)) { + $apiRequestData['customField'] = $customFieldsData; + } + + if ((isset($selectedOptions['selectedTags']) && !empty($selectedOptions['selectedTags'])) || !empty($actions)) { + $filterResponse = Hooks::apply(Config::withPrefix('high_level_contact_utilities'), $module, $selectedOptions, $actions); + + /** + * @deprecated 2.7.8 Use `bit_integrations_high_level_contact_utilities` filter instead. + * @since 2.7.8 + */ + $filterResponse = Hooks::apply('btcbi_high_level_contact_utilities', $filterResponse, $selectedOptions, $actions); + + if ($filterResponse !== $module && !empty($filterResponse)) { + $apiRequestData = array_merge($apiRequestData, $filterResponse); + } + } + + return $apiRequestData; + } + + private static function formatOpportunityData($finalData, $selectedOptions, $actions, $contactId, $module = 'opportunity') + { + $apiRequestData['title'] = $finalData['title']; + $apiRequestData['status'] = !empty($selectedOptions['selectedTaskStatus']) ? $selectedOptions['selectedTaskStatus'] : ''; + $apiRequestData['stageId'] = $selectedOptions['selectedStage']; + $apiRequestData['email'] = !empty($finalData['email']) ? $finalData['email'] : ''; + $apiRequestData['phone'] = !empty($finalData['phone']) ? $finalData['phone'] : ''; + $apiRequestData['assignedTo'] = !empty($selectedOptions['selectedUser']) ? $selectedOptions['selectedUser'] : ''; + $apiRequestData['monetaryValue'] = !empty($finalData['monetaryValue']) ? $finalData['monetaryValue'] : ''; + $apiRequestData['contactId'] = $contactId; + $apiRequestData['name'] = !empty($finalData['name']) ? $finalData['name'] : ''; + $apiRequestData['companyName'] = !empty($finalData['companyName']) ? $finalData['companyName'] : ''; + + if (!empty($selectedOptions['selectedTags'])) { + $filterResponse = Hooks::apply(Config::withPrefix('high_level_opportunity_utilities'), $module, $selectedOptions, $actions); + + /** + * @deprecated 2.7.8 Use `bit_integrations_high_level_opportunity_utilities` filter instead. + * @since 2.7.8 + */ + $filterResponse = Hooks::apply('btcbi_high_level_opportunity_utilities', $filterResponse, $selectedOptions, $actions); + + if ($filterResponse !== $module && !empty($filterResponse)) { + $apiRequestData = array_merge($apiRequestData, $filterResponse); + } + } + + return $apiRequestData; + } +} diff --git a/backend/Actions/HighLevel/Routes.php b/backend/Actions/HighLevel/Routes.php new file mode 100644 index 000000000..f0a95b402 --- /dev/null +++ b/backend/Actions/HighLevel/Routes.php @@ -0,0 +1,17 @@ + $data]; - if ($update && Helper::proActionFeatExists('Hubspot', 'updateEntity')) { + if ($update) { $id = $this->existsEntity('tickets', 'subject', $data['subject']); return empty($id) @@ -177,7 +178,7 @@ private function insertTicket($finalData, &$typeName) private function handleDeal($finalData, &$typeName, $update = false) { - if ($update && Helper::proActionFeatExists('Hubspot', 'updateEntity')) { + if ($update) { $id = $this->existsEntity('deals', 'dealname', $finalData['dealname']); return empty($id) @@ -242,9 +243,16 @@ private function insertContactOrCompany($finalData, $actionName, &$typeName) private function updateEntity($id, $finalData, $actionName, &$typeName) { $typeName = "{$actionName}-update"; - $response = apply_filters('btcbi_hubspot_update_entity', $id, $finalData, $actionName, $this->defaultHeader); + $response = Hooks::apply(Config::withPrefix('hubspot_update_entity'), $id, $finalData, $actionName, $this->defaultHeader); + + /** + * @deprecated 2.7.8 Use `bit_integrations_hubspot_update_entity` filter instead. + * @since 2.7.8 + */ + $response = Hooks::apply('btcbi_hubspot_update_entity', $response, $id, $finalData, $actionName, $this->defaultHeader); if (\is_string($response) && $response == $id) { + // translators: %s: Plugin name return (object) ['errors' => wp_sprintf(__('%s is not active or not installed', 'bit-integrations'), 'Bit Integrations Pro')]; } diff --git a/backend/Actions/Hubspot/Routes.php b/backend/Actions/Hubspot/Routes.php new file mode 100644 index 000000000..d3be0f01c --- /dev/null +++ b/backend/Actions/Hubspot/Routes.php @@ -0,0 +1,16 @@ +actionName; if (empty($fieldMap) || empty($authToken) || empty($actionName)) { + // translators: %s: Placeholder value return new WP_Error('REQ_FIELD_EMPTY', wp_sprintf(__('module, fields are required for %s api', 'bit-integrations'), 'Insightly')); } diff --git a/backend/Actions/Insightly/RecordApiHelper.php b/backend/Actions/Insightly/RecordApiHelper.php new file mode 100644 index 000000000..58d4c9f01 --- /dev/null +++ b/backend/Actions/Insightly/RecordApiHelper.php @@ -0,0 +1,288 @@ +integrationDetails = $integrationDetails; + $this->integrationId = $integId; + $this->apiUrl = $integrationDetails->api_url; + $this->defaultHeader = [ + 'Authorization' => 'Basic ' . base64_encode("{$integrationDetails->api_key}:"), + 'Content-Type' => 'application/json' + ]; + } + + public function addOrganisation($finalData) + { + if (empty($finalData['ORGANISATION_NAME'])) { + return ['success' => false, 'message' => __('Required field Name is empty', 'bit-integrations'), 'code' => 400]; + } + + $staticFieldsKeys = ['ORGANISATION_NAME', 'PHONE', 'PHONE_FAX', 'WEBSITE', 'SOCIAL_FACEBOOK', 'SOCIAL_LINKEDIN', 'SOCIAL_TWITTER', 'ADDRESS_BILLING_STREET', 'ADDRESS_BILLING_CITY', 'ADDRESS_BILLING_STATE', 'ADDRESS_BILLING_COUNTRY', 'ADDRESS_BILLING_POSTCODE', 'ADDRESS_SHIP_STREET', 'ADDRESS_SHIP_STATE', 'ADDRESS_SHIP_POSTCODE', 'ADDRESS_SHIP_COUNTRY']; + + foreach ($finalData as $key => $value) { + if (\in_array($key, $staticFieldsKeys)) { + $requestParams[$key] = $value; + } else { + $requestParams['CUSTOMFIELDS'][] = (object) [ + 'FIELD_NAME' => $key, + 'FIELD_VALUE' => $value + ]; + } + } + + $this->type = 'Organisation'; + $this->typeName = 'Organisation created'; + + $apiEndpoint = 'https://api.' . $this->apiUrl . '/v3.1/Organisations'; + + return $response = HttpHelper::post($apiEndpoint, wp_json_encode($requestParams), $this->defaultHeader); + } + + public function addContact($finalData) + { + if (empty($finalData['FIRST_NAME']) || empty($finalData['EMAIL_ADDRESS'])) { + return ['success' => false, 'message' => __('Required field Name or Email is empty', 'bit-integrations'), 'code' => 400]; + } + + $staticFieldsKeys = ['FIRST_NAME', 'LAST_NAME', 'TITLE', 'EMAIL_ADDRESS', 'PHONE', 'DATE_OF_BIRTH', 'SOCIAL_FACEBOOK', 'SOCIAL_LINKEDIN', 'SOCIAL_TWITTER', 'ADDRESS_MAIL_STREET', 'ADDRESS_MAIL_CITY', 'ADDRESS_MAIL_STATE', 'ADDRESS_MAIL_COUNTRY', 'ADDRESS_MAIL_POSTCODE', 'ADDRESS_OTHER_STREET', 'ADDRESS_OTHER_STATE', 'ADDRESS_OTHER_POSTCODE', 'ADDRESS_OTHER_COUNTRY']; + + foreach ($finalData as $key => $value) { + if (\in_array($key, $staticFieldsKeys)) { + $requestParams[$key] = $value; + } else { + $requestParams['CUSTOMFIELDS'][] = (object) [ + 'FIELD_NAME' => $key, + 'FIELD_VALUE' => $value + ]; + } + } + + if ($this->integrationDetails->actions->organisation) { + $requestParams['ORGANISATION_ID'] = $this->integrationDetails->selectedOrganisation; + } + + $this->type = 'Contact'; + $this->typeName = 'Contact created'; + + $apiEndpoint = 'https://api.' . $this->apiUrl . '/v3.1/Contacts'; + + return $response = HttpHelper::post($apiEndpoint, wp_json_encode($requestParams), $this->defaultHeader); + } + + public function addOpportunity($finalData) + { + if (empty($finalData['OPPORTUNITY_NAME'])) { + return ['success' => false, 'message' => __('Required field opportunity name is empty', 'bit-integrations'), 'code' => 400]; + } + $staticFieldsKeys = ['OPPORTUNITY_NAME', 'OPPORTUNITY_DETAILS', 'BID_AMOUNT', 'ACTUAL_CLOSE_DATE', 'PROBABILITY', 'FORECAST_CLOSE_DATE']; + + foreach ($finalData as $key => $value) { + if (\in_array($key, $staticFieldsKeys)) { + $requestParams[$key] = $value; + } else { + $requestParams['CUSTOMFIELDS'][] = (object) [ + 'FIELD_NAME' => $key, + 'FIELD_VALUE' => $value + ]; + } + } + + if ($this->integrationDetails->actions->organisation) { + $requestParams['ORGANISATION_ID'] = $this->integrationDetails->selectedOrganisation; + } + + if ($this->integrationDetails->actions->selectedCRMPipeline) { + $requestParams['PIPELINE_ID'] = $this->integrationDetails->selectedCRMPipeline; + } + + if ($this->integrationDetails->actions->selectedCRMPipelineStages) { + $requestParams['STAGE_ID'] = $this->integrationDetails->selectedCRMPipelineStages; + } + + $this->type = 'Opportunity'; + $this->typeName = 'Opportunity created'; + + $apiEndpoint = 'https://api.' . $this->apiUrl . '/v3.1/Opportunities'; + + return $response = HttpHelper::post($apiEndpoint, wp_json_encode($requestParams), $this->defaultHeader); + } + + public function addProject($finalData) + { + if (empty($finalData['PROJECT_NAME'])) { + return ['success' => false, 'message' => __('Required field opportunity name is empty', 'bit-integrations'), 'code' => 400]; + } + $staticFieldsKeys = ['PROJECT_NAME', 'PROJECT_DETAILS', 'BID_AMOUNT', 'ACTUAL_CLOSE_DATE', 'PROBABILITY', 'FORECAST_CLOSE_DATE']; + + foreach ($finalData as $key => $value) { + if (\in_array($key, $staticFieldsKeys)) { + $requestParams[$key] = $value; + } else { + $requestParams['CUSTOMFIELDS'][] = (object) [ + 'FIELD_NAME' => $key, + 'FIELD_VALUE' => $value + ]; + } + } + + if ($this->integrationDetails->actions->organisation) { + $requestParams['ORGANISATION_ID'] = $this->integrationDetails->selectedOrganisation; + } + + if ($this->integrationDetails->actions->selectedCRMPipeline) { + $requestParams['PIPELINE_ID'] = $this->integrationDetails->selectedCRMPipeline; + } + + if ($this->integrationDetails->actions->selectedCRMPipelineStages) { + $requestParams['STAGE_ID'] = $this->integrationDetails->selectedCRMPipelineStages; + } + + $this->type = 'Opportunity'; + $this->typeName = 'Opportunity created'; + + $apiEndpoint = 'https://api.' . $this->apiUrl . '/v3.1/Opportunities'; + + return $response = HttpHelper::post($apiEndpoint, wp_json_encode($requestParams), $this->defaultHeader); + } + + public function addTask($finalData) + { + if (empty($finalData['TITLE'])) { + return ['success' => false, 'message' => __('Required field opportunity name is empty', 'bit-integrations'), 'code' => 400]; + } + $staticFieldsKeys = ['TITLE', 'DUE_DATE', 'COMPLETED_DATE_UTC', 'DETAILS', 'PERCENT_COMPLETE', 'START_DATE']; + + foreach ($finalData as $key => $value) { + if (\in_array($key, $staticFieldsKeys)) { + $requestParams[$key] = $value; + } else { + $requestParams['CUSTOMFIELDS'][] = (object) [ + 'FIELD_NAME' => $key, + 'FIELD_VALUE' => $value + ]; + } + } + + if ($this->integrationDetails->actions->category) { + $requestParams['CATEGORY_ID'] = $this->integrationDetails->selectedCategory; + } + + $this->type = 'Task'; + $this->typeName = 'Task created'; + + $apiEndpoint = 'https://api.' . $this->apiUrl . '/v3.1/Tasks'; + + return HttpHelper::post($apiEndpoint, wp_json_encode($requestParams), $this->defaultHeader); + } + + public function addLead($finalData) + { + if (empty($finalData['LAST_NAME'])) { + return ['success' => false, 'message' => __('Required field opportunity name is empty', 'bit-integrations'), 'code' => 400]; + } + $staticFieldsKeys = ['FIRST_NAME', 'LAST_NAME', 'TITLE', 'ORGANISATION_NAME', 'LEAD_RATING', 'EMAIL', 'PHONE', 'MOBILE', 'FAX', 'WEBSITE', 'INDUSTRY', 'EMPLOYEE_COUNT', 'ADDRESS_STREET', 'ADDRESS_CITY', 'ADDRESS_STATE', 'ADDRESS_POSTCODE', 'ADDRESS_COUNTRY', 'LEAD_DESCRIPTION']; + + foreach ($finalData as $key => $value) { + if (\in_array($key, $staticFieldsKeys)) { + $requestParams[$key] = $value; + } else { + $requestParams['CUSTOMFIELDS'][] = (object) [ + 'FIELD_NAME' => $key, + 'FIELD_VALUE' => $value + ]; + } + } + + if ($this->integrationDetails->actions->category) { + $requestParams['CATEGORY_ID'] = $this->integrationDetails->selectedCategory; + } + if ($this->integrationDetails->actions->category) { + $requestParams['LEAD_SOURCE_ID'] = $this->integrationDetails->selectedLeadStatus; + } + if ($this->integrationDetails->actions->category) { + $requestParams['LEAD_STATUS_ID'] = $this->integrationDetails->selectedLeadSource; + } + + $this->type = 'Lead'; + $this->typeName = 'Lead created'; + + $apiEndpoint = 'https://api.' . $this->apiUrl . '/v3.1/Leads'; + + return HttpHelper::post($apiEndpoint, wp_json_encode($requestParams), $this->defaultHeader); + } + + public function generateReqDataFromFieldMap($data, $fieldMap) + { + $dataFinal = []; + foreach ($fieldMap as $value) { + $triggerValue = $value->formField; + $actionValue = $value->insightlyFormField; + if ($triggerValue === 'custom') { + if ($actionValue === 'CUSTOMFIELDS') { + $dataFinal[$value->customFieldKey] = $value->customValue; + } else { + $dataFinal[$actionValue] = $value->customValue; + } + } elseif (!\is_null($data[$triggerValue])) { + if ($actionValue === 'CUSTOMFIELDS') { + $dataFinal[$value->customFieldKey] = $data[$triggerValue]; + } else { + $dataFinal[$actionValue] = $data[$triggerValue]; + } + } + } + + return $dataFinal; + } + + public function execute($fieldValues, $fieldMap, $actionName) + { + $finalData = $this->generateReqDataFromFieldMap($fieldValues, $fieldMap); + if ($actionName === 'organisation') { + $apiResponse = $this->addOrganisation($finalData); + } elseif ($actionName === 'contact') { + $apiResponse = $this->addContact($finalData); + } elseif ($actionName === 'opportunity') { + $apiResponse = $this->addOpportunity($finalData); + } elseif ($actionName === 'task') { + $apiResponse = $this->addTask($finalData); + } elseif ($actionName === 'lead') { + $apiResponse = $this->addLead($finalData); + } + + if ($apiResponse->CONTACT_ID || $apiResponse->status === 'success') { + $res = [$this->typeName . ' successfully']; + LogHandler::save($this->integrationId, wp_json_encode(['type' => $this->type, 'type_name' => $this->typeName]), 'success', wp_json_encode($res)); + } else { + LogHandler::save($this->integrationId, wp_json_encode(['type' => $this->type, 'type_name' => $this->type . ' creating']), 'error', wp_json_encode($apiResponse)); + } + + return $apiResponse; + } +} diff --git a/backend/Actions/Insightly/Routes.php b/backend/Actions/Insightly/Routes.php new file mode 100644 index 000000000..eebb96fcd --- /dev/null +++ b/backend/Actions/Insightly/Routes.php @@ -0,0 +1,17 @@ +_integrationID = $integId; + } + + public function createPostType($finalData, $createCPTSelectedOptions, $actions) + { + if (empty($finalData['name'])) { + return ['success' => false, 'message' => __('Request parameters are empty!', 'bit-integrations'), 'code' => 400]; + } + + $finalData['slug'] = str_replace(' ', '-', strtolower($finalData['name'])); + + $filterResponse = Hooks::apply(Config::withPrefix('jet_engine_create_post_type_actions'), 'createPostType', $createCPTSelectedOptions, $actions); + + /** + * @deprecated 2.7.8 Use `bit_integrations_jet_engine_create_post_type_actions` filter instead. + * @since 2.7.8 + */ + $filterResponse = Hooks::apply('btcbi_jet_engine_create_post_type_actions', $filterResponse, $createCPTSelectedOptions, $actions); + + if ($filterResponse !== 'createPostType' && !empty($filterResponse)) { + $finalData = array_merge($finalData, $filterResponse); + } + + jet_engine()->cpt->data->set_request($finalData); + + $postTypeId = jet_engine()->cpt->data->create_item(false); + + if (empty($postTypeId) || is_wp_error($postTypeId)) { + return ['success' => false, 'message' => __('Failed to add post type!', 'bit-integrations'), 'code' => 400]; + } + + return ['success' => true, 'message' => __('Post type created successfully', 'bit-integrations')]; + } + + public function createContentType($finalData, $createCPTSelectedOptions, $actions) + { + if (!jet_engine()->modules->is_module_active('custom-content-types')) { + return ['success' => false, 'message' => __('Module - Custom Content Type is not active!', 'bit-integrations'), 'code' => 400]; + } + + if (empty($finalData['name'])) { + return ['success' => false, 'message' => __('Request parameters are empty!', 'bit-integrations'), 'code' => 400]; + } + + $ctcData['name'] = $finalData['name']; + $ctcData['slug'] = str_replace(' ', '_', strtolower($finalData['name'])); + $args = $ctcData; + + if (isset($finalData['capability'])) { + $args['capability'] = $finalData['capability']; + } + + $filterResponse = Hooks::apply(Config::withPrefix('jet_engine_create_content_type_actions'), 'createContentType', $createCPTSelectedOptions, $actions); + + /** + * @deprecated 2.7.8 Use `bit_integrations_jet_engine_create_content_type_actions` filter instead. + * @since 2.7.8 + */ + $filterResponse = Hooks::apply('btcbi_jet_engine_create_content_type_actions', $filterResponse, $createCPTSelectedOptions, $actions); + + if ($filterResponse !== 'createContentType' && !empty($filterResponse)) { + $args = array_merge($args, $filterResponse); + } + + $ctcData['args'] = $args; + $ctcData['meta_fields'] = []; + + Module::instance()->manager->data->set_request($ctcData); + + $itemId = Module::instance()->manager->data->create_item(false); + + if (empty($itemId) || is_wp_error($itemId)) { + return ['success' => false, 'message' => __('Failed to add custom content type!', 'bit-integrations'), 'code' => 400]; + } + + return ['success' => true, 'message' => __('Custom content type created successfully', 'bit-integrations')]; + } + + public function createTaxonomy($finalData, $taxOptions, $actions) + { + if (empty($finalData['name']) || empty($taxOptions['selectedTaxPostTypes'])) { + return ['success' => false, 'message' => __('Request parameters are empty!', 'bit-integrations'), 'code' => 400]; + } + + $finalData['slug'] = str_replace(' ', '-', strtolower($finalData['name'])); + $finalData['object_type'] = explode(',', $taxOptions['selectedTaxPostTypes']); + + $filterResponse = Hooks::apply(Config::withPrefix('jet_engine_create_taxonomy_actions'), 'createTaxonomy', $taxOptions, $actions); + + /** + * @deprecated 2.7.8 Use `bit_integrations_jet_engine_create_taxonomy_actions` filter instead. + * @since 2.7.8 + */ + $filterResponse = Hooks::apply('btcbi_jet_engine_create_taxonomy_actions', $filterResponse, $taxOptions, $actions); + + if ($filterResponse !== 'createTaxonomy' && !empty($filterResponse)) { + $finalData = array_merge($finalData, $filterResponse); + } + + jet_engine()->taxonomies->data->set_request($finalData); + + $taxId = jet_engine()->taxonomies->data->create_item(false); + + if (empty($taxId) || is_wp_error($taxId)) { + return ['success' => false, 'message' => __('Failed to add taxonomy!', 'bit-integrations'), 'code' => 400]; + } + + return ['success' => true, 'message' => __('Taxonomy added successfully', 'bit-integrations')]; + } + + public function createRelation($finalData, $relOptions, $actions) + { + if ( + empty($relOptions) || empty($relOptions['parentObject']) + || empty($relOptions['childObject']) || empty($relOptions['selectedRelationType']) + ) { + return ['success' => false, 'message' => __('Request parameters are empty!', 'bit-integrations'), 'code' => 400]; + } + + $args['parent_object'] = $relOptions['parentObject']; + $args['child_object'] = $relOptions['childObject']; + $args['type'] = $relOptions['selectedRelationType']; + $args['labels'] = $finalData; + + $filterResponse = Hooks::apply(Config::withPrefix('jet_engine_create_relation_actions'), 'createRelation', $relOptions, $actions); + + /** + * @deprecated 2.7.8 Use `bit_integrations_jet_engine_create_relation_actions` filter instead. + * @since 2.7.8 + */ + $filterResponse = Hooks::apply('btcbi_jet_engine_create_relation_actions', $filterResponse, $relOptions, $actions); + + if ($filterResponse !== 'createRelation' && !empty($filterResponse)) { + $args = array_merge($args, $filterResponse); + } + + jet_engine()->relations->data->set_request($args); + + $itemId = jet_engine()->relations->data->create_item(false); + + if (empty($itemId) || is_wp_error($itemId)) { + return ['success' => false, 'message' => __('Failed to add new relation!', 'bit-integrations'), 'code' => 400]; + } + + return ['success' => true, 'message' => __('Relation added successfully', 'bit-integrations')]; + } + + public function updatePostType($finalData, $createCPTSelectedOptions, $actions) + { + if (empty($createCPTSelectedOptions) || empty($createCPTSelectedOptions['selectedCPT'])) { + return ['success' => false, 'message' => 'Request parameters are empty!', 'code' => 400]; + } + + $id = $createCPTSelectedOptions['selectedCPT']; + + if (empty($id)) { + return ['success' => false, 'message' => 'Custom post type id not found in request!', 'code' => 400]; + } + + $initialPostType = jet_engine()->cpt->data->get_item_for_edit($id); + $initialSlug = $initialPostType['general_settings']['slug']; + $initialName = $initialPostType['general_settings']['name']; + + $finalData['id'] = $id; + + if (!empty($finalData['name'])) { + $finalData['slug'] = str_replace(' ', '-', strtolower($finalData['name'])); + } + + $filterResponse = Hooks::apply(Config::withPrefix('jet_engine_create_post_type_actions'), 'updatePostType', $createCPTSelectedOptions, $actions); + + /** + * @deprecated 2.7.8 Use `bit_integrations_jet_engine_create_post_type_actions` filter instead. + * @since 2.7.8 + */ + $filterResponse = Hooks::apply('btcbi_jet_engine_create_post_type_actions', $filterResponse, $createCPTSelectedOptions, $actions); + + if ($filterResponse !== 'updatePostType' && !empty($filterResponse)) { + $finalData = array_merge($finalData, $filterResponse); + } + + if (empty($finalData['name'])) { + $finalData['name'] = $initialName; + } + + if (empty($finalData['slug'])) { + $finalData['slug'] = $initialSlug; + } + + jet_engine()->cpt->data->set_request($finalData); + + $updated = jet_engine()->cpt->data->edit_item(false); + + if (empty($updated) || is_wp_error($updated)) { + return ['success' => false, 'message' => 'Failed to update post type!', 'code' => 400]; + } + + if ($updated && !empty($initialSlug) && !empty($finalData['slug']) && $initialSlug !== $finalData['slug']) { + global $wpdb; + + // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Direct update needed for JetEngine post type slug change + $wpdb->update( + $wpdb->posts, + [ + 'post_type' => $finalData['slug'], + ], + [ + 'post_type' => $initialSlug, + ] + ); + } + + return ['success' => true, 'message' => 'Post type updated successfully.']; + } + + public function updateContentType($finalData, $createCPTSelectedOptions, $actions) + { + if (!jet_engine()->modules->is_module_active('custom-content-types')) { + return ['success' => false, 'message' => 'Module - Custom Content Type is not active!', 'code' => 400]; + } + + $id = $createCPTSelectedOptions['selectedCCT']; + + if (empty($id)) { + return ['success' => false, 'message' => 'Custom Content type id not found in request!', 'code' => 400]; + } + + $ctcData['id'] = $id; + + $initialContentType = Module::instance()->manager->data->get_item_for_edit($id); + $initialSlug = $initialContentType['slug']; + $initialName = $initialContentType['name']; + + if (!empty($finalData['name'])) { + $ctcData['name'] = $finalData['name']; + $ctcData['slug'] = str_replace(' ', '_', strtolower($finalData['name'])); + } else { + $ctcData['name'] = $initialName; + $ctcData['slug'] = $initialSlug; + } + + $args = $ctcData; + + if (isset($finalData['capability'])) { + $args['capability'] = $finalData['capability']; + } + + $filterResponse = Hooks::apply(Config::withPrefix('jet_engine_create_content_type_actions'), 'updateContentType', $createCPTSelectedOptions, $actions); + + /** + * @deprecated 2.7.8 Use `bit_integrations_jet_engine_create_content_type_actions` filter instead. + * @since 2.7.8 + */ + $filterResponse = Hooks::apply('btcbi_jet_engine_create_content_type_actions', $filterResponse, $createCPTSelectedOptions, $actions); + + if ($filterResponse !== 'updateContentType' && !empty($filterResponse)) { + $args = array_merge($args, $filterResponse); + } + + $ctcData['args'] = $args; + $ctcData['meta_fields'] = []; + + Module::instance()->manager->data->set_request($ctcData); + + $updated = Module::instance()->manager->data->edit_item(false); + + if (!$updated || is_wp_error($updated)) { + return ['success' => false, 'message' => 'Failed to update content type!', 'code' => 400]; + } + + return ['success' => true, 'message' => 'Content type updated successfully.']; + } + + public function updateTaxonomy($finalData, $taxOptions, $actions) + { + $id = $taxOptions['selectedTaxForEdit']; + + if (empty($id)) { + return ['success' => false, 'message' => 'Taxonomy id not found in request!', 'code' => 400]; + } + + $initialTaxonomy = jet_engine()->taxonomies->data->get_item_for_edit($id); + $initialName = $initialTaxonomy['general_settings']['name']; + $initialSlug = $initialTaxonomy['general_settings']['slug']; + + $finalData['id'] = $id; + + if (!empty($taxOptions['selectedTaxPostTypes'])) { + $finalData['object_type'] = explode(',', $taxOptions['selectedTaxPostTypes']); + } + + if (!empty($finalData['name'])) { + $finalData['slug'] = str_replace(' ', '-', strtolower($finalData['name'])); + } else { + $finalData['name'] = $initialName; + $finalData['slug'] = $initialSlug; + } + + $filterResponse = Hooks::apply(Config::withPrefix('jet_engine_create_taxonomy_actions'), 'updateTaxonomy', $taxOptions, $actions); + + /** + * @deprecated 2.7.8 Use `bit_integrations_jet_engine_create_taxonomy_actions` filter instead. + * @since 2.7.8 + */ + $filterResponse = Hooks::apply('btcbi_jet_engine_create_taxonomy_actions', $filterResponse, $taxOptions, $actions); + + if ($filterResponse !== 'updateTaxonomy' && !empty($filterResponse)) { + $finalData = array_merge($finalData, $filterResponse); + } + + jet_engine()->taxonomies->data->set_request($finalData); + + $updated = jet_engine()->taxonomies->data->edit_item(false); + + if (!$updated || is_wp_error($updated)) { + return ['success' => false, 'message' => 'Failed to update taxonomy!', 'code' => 400]; + } + + return ['success' => true, 'message' => 'Taxonomy updated successfully.']; + } + + public function updateRelation($finalData, $relOptions, $actions) + { + if ( + empty($relOptions) || empty($relOptions['parentObject']) + || empty($relOptions['childObject']) || empty($relOptions['selectedRelationType']) + ) { + return ['success' => false, 'message' => 'Request parameters are empty!', 'code' => 400]; + } + + $args['parent_object'] = $relOptions['parentObject']; + $args['child_object'] = $relOptions['childObject']; + $args['type'] = $relOptions['selectedRelationType']; + + if (empty($relOptions['selectedRelationForEdit'])) { + return ['success' => false, 'message' => 'Relation id not found in request!', 'code' => 400]; + } + + $id = $relOptions['selectedRelationForEdit']; + $initialRelation = jet_engine()->relations->data->get_item_for_edit($id); + $labels = maybe_unserialize($initialRelation['labels']); + $initialName = $labels['name']; + $args['id'] = $id; + + if (empty($finalData['name'])) { + $finalData['name'] = $initialName; + } + + $args['labels'] = $finalData; + + $filterResponse = Hooks::apply(Config::withPrefix('jet_engine_create_relation_actions'), 'updateRelation', $relOptions, $actions); + + /** + * @deprecated 2.7.8 Use `bit_integrations_jet_engine_create_relation_actions` filter instead. + * @since 2.7.8 + */ + $filterResponse = Hooks::apply('btcbi_jet_engine_create_relation_actions', $filterResponse, $relOptions, $actions); + + if ($filterResponse !== 'updateRelation' && !empty($filterResponse)) { + $args = array_merge($args, $filterResponse); + } + + jet_engine()->relations->data->set_request($args); + + $updated = jet_engine()->relations->data->edit_item(false); + + if (!$updated || is_wp_error($updated)) { + return ['success' => false, 'message' => 'Failed to update relation!', 'code' => 400]; + } + + return ['success' => true, 'message' => 'Relation updated successfully.']; + } + + public function deletePostType($finalData, $selectedCPT, $actions) + { + if (empty($finalData['post_type_id']) && empty($selectedCPT)) { + return ['success' => false, 'message' => 'Post type id not found in request!', 'code' => 400]; + } + + if (!empty($selectedCPT)) { + $id = $selectedCPT; + } else { + $id = $finalData['post_type_id']; + } + + $postTypeData = jet_engine()->cpt->data->get_item_for_edit($id); + + if (!$postTypeData || !isset($postTypeData['general_settings']['slug'])) { + return ['success' => false, 'message' => 'Post type data not found!', 'code' => 400]; + } + + if (isset($actions['delete_all_posts']) && $actions['delete_all_posts']) { + $fromPostType = $postTypeData['general_settings']['slug']; + + $posts = get_posts([ + 'post_type' => $fromPostType, + 'post_status' => 'any', + 'posts_per_page' => -1, + 'fields' => 'ids' + ]); + + if (!empty($posts) && !is_wp_error($posts)) { + foreach ($posts as $postId) { + wp_delete_post($postId, true); + } + } + + if (!empty($postTypeData['general_settings']['custom_storage']) && $postTypeData['general_settings']['custom_storage'] === true) { + $db = \Jet_Engine\CPT\Custom_Tables\Manager::instance()->get_db_instance($fromPostType, []); + $db->drop_table(); + } + } + + jet_engine()->cpt->data->set_request(['id' => $id]); + + if (jet_engine()->cpt->data->delete_item(false)) { + return ['success' => true, 'message' => 'Post Type deleted successfully.']; + } + + return ['success' => false, 'message' => 'Failed to delete post type!', 'code' => 400]; + } + + public function deleteContentType($finalData, $selectedCCT) + { + if (!jet_engine()->modules->is_module_active('custom-content-types')) { + return ['success' => false, 'message' => 'Module - Custom Content Type is not active!', 'code' => 400]; + } + + if (empty($selectedCCT) && empty($finalData['content_type_id'])) { + return ['success' => false, 'message' => 'Content type id not found in request!', 'code' => 400]; + } + + if (!empty($selectedCCT)) { + $id = $selectedCCT; + } else { + $id = $finalData['content_type_id']; + } + + Module::instance()->manager->data->set_request(['id' => $id]); + + if (Module::instance()->manager->data->delete_item(false)) { + return ['success' => true, 'message' => 'Content Type deleted successfully.']; + } + + return ['success' => false, 'message' => 'Failed to delete content type!', 'code' => 400]; + } + + public function deleteTaxonomy($finalData, $selectedTax, $actions) + { + if (empty($selectedTax) && empty($finalData['tax_id'])) { + return ['success' => false, 'message' => 'Taxonomy id not found in request!', 'code' => 400]; + } + + if (!empty($selectedTax)) { + $id = $selectedTax; + } else { + $id = $finalData['tax_id']; + } + + $taxData = jet_engine()->taxonomies->data->get_item_for_edit($id); + + if (!$taxData || !isset($taxData['general_settings']['slug'])) { + return ['success' => false, 'message' => 'Taxonomy data not found!', 'code' => 400]; + } + + if (isset($actions['delete_all_tax_terms']) && $actions['delete_all_tax_terms']) { + $fromTax = $taxData['general_settings']['slug']; + + $terms = get_terms([ + 'taxonomy' => $fromTax, + 'hide_empty' => false, + 'fields' => 'ids', + ]); + + if (!empty($terms) && !is_wp_error($terms)) { + foreach ($terms as $termId) { + wp_delete_term($termId, $fromTax); + } + } + } + + jet_engine()->taxonomies->data->set_request(['id' => $id]); + + if (jet_engine()->taxonomies->data->delete_item(false)) { + return ['success' => true, 'message' => 'Taxonomy deleted successfully.']; + } + + return ['success' => false, 'message' => 'Failed to delete taxonomy!', 'code' => 400]; + } + + public function deleteRelation($finalData, $selectedRelation) + { + if (empty($selectedRelation) && empty($finalData['relation_id'])) { + return ['success' => false, 'message' => 'Relation id not found in request!', 'code' => 400]; + } + + if (!empty($selectedRelation)) { + $id = $selectedRelation; + } else { + $id = $finalData['relation_id']; + } + + jet_engine()->relations->data->set_request(['id' => $id]); + + if (jet_engine()->relations->data->delete_item(false)) { + return ['success' => true, 'message' => 'Relation deleted successfully.']; + } + + return ['success' => false, 'message' => 'Failed to delete relation!', 'code' => 400]; + } + + public function generateReqDataFromFieldMap($data, $fieldMap) + { + $dataFinal = []; + foreach ($fieldMap as $value) { + $triggerValue = $value->formField; + $actionValue = $value->jetEngineField; + if ($triggerValue === 'custom') { + $dataFinal[$actionValue] = Common::replaceFieldWithValue($value->customValue, $data); + } elseif (!\is_null($data[$triggerValue])) { + $dataFinal[$actionValue] = $data[$triggerValue]; + } + } + + return $dataFinal; + } + + public function execute($fieldValues, $fieldMap, $selectedTask, $actions, $createCPTSelectedOptions, $taxOptions, $relOptions) + { + if (isset($fieldMap[0]) && empty($fieldMap[0]->formField)) { + $finalData = []; + } else { + $finalData = $this->generateReqDataFromFieldMap($fieldValues, $fieldMap); + } + + $type = $typeName = ''; + + if ($selectedTask === 'createPostType') { + $response = $this->createPostType($finalData, $createCPTSelectedOptions, $actions); + $type = 'Post Type'; + $typeName = 'Create Post Type'; + } elseif ($selectedTask === 'createContentType') { + $response = $this->createContentType($finalData, $createCPTSelectedOptions, $actions); + $type = 'Content Type'; + $typeName = 'Create Content Type'; + } elseif ($selectedTask === 'createTaxonomy') { + $response = $this->createTaxonomy($finalData, $taxOptions, $actions); + $type = 'Taxonomy'; + $typeName = 'Create Taxonomy'; + } elseif ($selectedTask === 'createRelation') { + $response = $this->createRelation($finalData, $relOptions, $actions); + $type = 'Relation'; + $typeName = 'Create Relation'; + } elseif ($selectedTask === 'updatePostType') { + $response = $this->updatePostType($finalData, $createCPTSelectedOptions, $actions); + $type = 'Post Type'; + $typeName = 'Update Post Type'; + } elseif ($selectedTask === 'updateContentType') { + $response = $this->updateContentType($finalData, $createCPTSelectedOptions, $actions); + $type = 'Content Type'; + $typeName = 'Update Content Type'; + } elseif ($selectedTask === 'updateTaxonomy') { + $response = $this->updateTaxonomy($finalData, $taxOptions, $actions); + $type = 'Taxonomy'; + $typeName = 'Update Taxonomy'; + } elseif ($selectedTask === 'updateRelation') { + $response = $this->updateRelation($finalData, $relOptions, $actions); + $type = 'Relation'; + $typeName = 'Update Relation'; + } elseif ($selectedTask === 'deletePostType') { + $selectedCPT = $createCPTSelectedOptions['selectedCPT']; + $response = $this->deletePostType($finalData, $selectedCPT, $actions); + $type = 'Post Type'; + $typeName = 'Delete Post Type'; + } elseif ($selectedTask === 'deleteContentType') { + $selectedCCT = $createCPTSelectedOptions['selectedCCT']; + $response = $this->deleteContentType($finalData, $selectedCCT); + $type = 'Content Type'; + $typeName = 'Delete Content Type'; + } elseif ($selectedTask === 'deleteTaxonomy') { + $selectedTax = $taxOptions['selectedTaxForEdit']; + $response = $this->deleteTaxonomy($finalData, $selectedTax, $actions); + $type = 'Taxonomy'; + $typeName = 'Delete Taxonomy'; + } elseif ($selectedTask === 'deleteRelation') { + $selectedRelation = $relOptions['selectedRelationForEdit']; + $response = $this->deleteRelation($finalData, $selectedRelation); + $type = 'Relation'; + $typeName = 'Delete Relation'; + } + + if ($response['success']) { + $res = ['message' => $response['message']]; + LogHandler::save($this->_integrationID, wp_json_encode(['type' => $type, 'type_name' => $typeName]), 'success', wp_json_encode($res)); + } else { + LogHandler::save($this->_integrationID, wp_json_encode(['type' => $type, 'type_name' => $typeName]), 'error', wp_json_encode($response)); + } + + return $response; + } +} diff --git a/backend/Actions/JetEngine/Routes.php b/backend/Actions/JetEngine/Routes.php new file mode 100644 index 000000000..4025eecaa --- /dev/null +++ b/backend/Actions/JetEngine/Routes.php @@ -0,0 +1,19 @@ +_defaultHeader['Content-Type'] = 'application/json'; + $this->_tokenDetails = $tokenDetails; + $this->_integrationID = $integId; + } + + public function insertCard($data) + { + $data = \is_string($data) ? $data : wp_json_encode((object) $data); + + $header['Authorization'] = "Bearer {$this->_tokenDetails->access_token}"; + $header['Content-Type'] = 'application/json'; + $insertRecordEndpoint = 'https://api.infusionsoft.com/crm/rest/v1/contacts'; + + return HttpHelper::post($insertRecordEndpoint, $data, $header); + } + + public function insertTag($contactId, $tags) + { + $tagIds = explode(',', $tags); + $allTagIds = []; + foreach ($tagIds as $tag) { + $allTagIds[] = (int) $tag; + } + + $data['tagIds'] = $allTagIds; + + $header['Authorization'] = "Bearer {$this->_tokenDetails->access_token}"; + $header['Content-Type'] = 'application/json'; + $insertTagEndpoint = 'https://api.infusionsoft.com/crm/rest/v1/contacts/' . $contactId . '/tags'; + + return $response = HttpHelper::post($insertTagEndpoint, wp_json_encode($data), $header); + } + + public function generateReqDataFromFieldMap($data, $fieldMap) + { + $dataFinal = []; + $billing_address = [ + 'field' => 'BILLING', + ]; + $shipping_address = [ + 'field' => 'SHIPPING', + ]; + $restOfdata = []; + $anotherData = []; + $customFields = []; + + foreach ($fieldMap as $key => $value) { + $triggerValue = $value->formField; + $actionValue = $value->keapField; + + if ($triggerValue === 'custom' && str_starts_with($actionValue, 'custom_fields_')) { + $customFields[] = (object) [ + 'id' => str_replace('custom_fields_', '', $actionValue), + 'content' => Common::replaceFieldWithValue($value->customValue, $data) + ]; + } elseif ($triggerValue === 'custom') { + $dataFinal[$actionValue] = Common::replaceFieldWithValue($value->customValue, $data); + } elseif (!\is_null($data[$triggerValue]) && str_starts_with($actionValue, 'custom_fields_')) { + $customFields[] = (object) [ + 'id' => str_replace('custom_fields_', '', $actionValue), + 'content' => $data[$triggerValue] + ]; + } elseif (!\is_null($data[$triggerValue])) { + if ($actionValue === 'billing_country_code') { + $billing_address = $billing_address + ['country_code' => $data[$triggerValue]]; + } elseif ($actionValue === 'billing_locality') { + $billing_address = $billing_address + ['locality' => $data[$triggerValue]]; + } elseif ($actionValue === 'billing_first_address_street') { + $billing_address = $billing_address + ['line1' => $data[$triggerValue]]; + } elseif ($actionValue === 'billing_second_address_street') { + $billing_address = $billing_address + ['line2' => $data[$triggerValue]]; + } elseif ($actionValue === 'billing_zip_code') { + $billing_address = $billing_address + ['zip_code' => $data[$triggerValue]]; + } elseif ($actionValue === 'shipping_country_code') { + $shipping_address = $shipping_address + ['country_code' => $data[$triggerValue]]; + } elseif ($actionValue === 'shipping_locality') { + $shipping_address = $shipping_address + ['locality' => $data[$triggerValue]]; + } elseif ($actionValue === 'shipping_first_address_street') { + $shipping_address = $shipping_address + ['line1' => $data[$triggerValue]]; + } elseif ($actionValue === 'shipping_second_address_street') { + $shipping_address = $shipping_address + ['line2' => $data[$triggerValue]]; + } elseif ($actionValue === 'shipping_zip_code') { + $shipping_address = $shipping_address + ['zip_code' => $data[$triggerValue]]; + } elseif ($actionValue === 'email_addresses') { + $restOfdata[$actionValue] = [(object) [ + 'email' => $data[$triggerValue], + 'field' => 'EMAIL1' + ]]; + } elseif ($actionValue === 'fax_numbers') { + $restOfdata[$actionValue] = [(object) [ + 'field' => 'FAX1', + 'number' => $data[$triggerValue], + 'type' => 'string' + ]]; + } elseif ($value->keapField === 'phone_numbers') { + $restOfdata[$actionValue] = [(object) [ + 'extension' => 'string', + 'field' => 'PHONE1', + 'number' => $data[$triggerValue], + ]]; + } else { + $anotherData[$actionValue] = $data[$triggerValue]; + } + } + } + $addressMergeData['addresses'][0] = (object) $billing_address; + $addressMergeData['addresses'][1] = (object) $shipping_address; + + return $addressMergeData + $restOfdata + $anotherData + ['custom_fields' => $customFields]; + } + + public function execute($defaultConf, $fieldValues, $fieldMap, $actions) + { + $fieldData = []; + $finalData = $this->generateReqDataFromFieldMap($fieldValues, $fieldMap); + $apiResponse = $this->insertCard($finalData); + + if ($defaultConf->actions->tags || isset($apiResponse->id)) { + $tagResponse = $this->insertTag($apiResponse->id, $defaultConf->selectedTags); + } + + if (!(isset($apiResponse->id))) { + LogHandler::save($this->_integrationID, ['type' => 'contact', 'type_name' => 'add-contact'], 'error', $apiResponse); + } else { + LogHandler::save($this->_integrationID, ['type' => 'record', 'type_name' => 'add-contact'], 'success', $apiResponse); + } + + return $apiResponse; + } +} diff --git a/backend/Actions/Keap/Routes.php b/backend/Actions/Keap/Routes.php new file mode 100644 index 000000000..ed246acf8 --- /dev/null +++ b/backend/Actions/Keap/Routes.php @@ -0,0 +1,12 @@ +_integrationID = $integrationId; + } + + public function generateReqDataFromFieldMap($data, $fieldMap) + { + $dataFinal = []; + + foreach ($fieldMap as $key => $value) { + $triggerValue = $value->formField; + $actionValue = $value->kirimEmailFormField; + if ($triggerValue === 'custom') { + $dataFinal[$actionValue] = Common::replaceFieldWithValue($value->customValue, $data); + } elseif (!\is_null($data[$triggerValue])) { + $dataFinal[$actionValue] = $data[$triggerValue]; + } + } + + return $dataFinal; + } + + public function addSubscriber($api_key, $userName, $listId, $finalData) + { + $time = time(); + $generated_token = hash_hmac('sha256', "{$userName}" . '::' . "{$api_key}" . '::' . $time, "{$api_key}"); + $header = [ + 'Auth-Id' => $userName, + 'Auth-Token' => $generated_token, + 'Timestamp' => $time, + 'Content-Type' => 'application/x-www-form-urlencoded' + ]; + + $apiEndpoint = 'https://api.kirim.email/v3/subscriber/'; + + $data = array_merge($finalData, ['lists' => $listId]); + + return HttpHelper::post($apiEndpoint, wp_json_encode($data), $header); + } + + public function deleteSubscriber($api_key, $userName, $listId, $finalData) + { + $time = time(); + $generated_token = hash_hmac('sha256', "{$userName}" . '::' . "{$api_key}" . '::' . $time, "{$api_key}"); + $header = [ + 'Auth-Id' => $userName, + 'Auth-Token' => $generated_token, + 'Timestamp' => $time, + ]; + + $apiEndpoint = "https://api.kirim.email/v3/subscriber/email/{$finalData['email']}"; + $apiRes = HttpHelper::get($apiEndpoint, null, $header); + + if (isset($apiRes->status) && $apiRes->status == 'success') { + $subscriberId = $apiRes->data->id; + $listIdBySearchMail = $apiRes->data->list[0]->id; + $time = time(); + $generated_token = hash_hmac('sha256', "{$userName}" . '::' . "{$api_key}" . '::' . $time, "{$api_key}"); + $header = [ + 'Auth-Id' => $userName, + 'Auth-Token' => $generated_token, + 'Timestamp' => $time, + 'List-Id' => $listIdBySearchMail, + ]; + $apiEndpointDelete = "https://api.kirim.email/v3/subscriber/{$subscriberId}"; + + return HttpHelper::request($apiEndpointDelete, 'DELETE', null, $header); + } + + return false; + } + + public function execute( + $api_key, + $userName, + $fieldValues, + $fieldMap, + $integrationDetails, + $mainAction + ) { + $finalData = $this->generateReqDataFromFieldMap($fieldValues, $fieldMap); + + if ($mainAction == '1') { + $listId = $integrationDetails->listId; + $apiResponse = $this->addSubscriber($api_key, $userName, $listId, $finalData); + if (isset($apiResponse->code) && $apiResponse->code == 200) { + LogHandler::save($this->_integrationID, wp_json_encode(['type' => 'insert', 'type_name' => 'add-subscriber']), 'success', wp_json_encode($apiResponse)); + } else { + LogHandler::save($this->_integrationID, wp_json_encode(['type' => 'insert', 'type_name' => 'add-subscriber']), 'error', wp_json_encode($apiResponse)); + } + } + if ($mainAction == '2') { + $listId = $integrationDetails->listId; + $apiResponse = $this->deleteSubscriber($api_key, $userName, $listId, $finalData); + if (isset($apiResponse->code) && $apiResponse->code == 200) { + LogHandler::save($this->_integrationID, wp_json_encode(['type' => 'delete', 'type_name' => 'delete-subscriber']), 'success', wp_json_encode($apiResponse->message)); + } else { + LogHandler::save($this->_integrationID, wp_json_encode(['type' => 'delete', 'type_name' => 'delete-subscriber']), 'error', wp_json_encode(__('Subscriber not found , failed to delete subscriber', 'bit-integrations'))); + } + } + + return $apiResponse; + } +} diff --git a/backend/Actions/KirimEmail/Routes.php b/backend/Actions/KirimEmail/Routes.php new file mode 100644 index 000000000..541d3b3db --- /dev/null +++ b/backend/Actions/KirimEmail/Routes.php @@ -0,0 +1,11 @@ +_integrationDetails = $integrationDetails; + $this->_integrationID = $integId; + } + + public function generateReqDataFromFieldMap($data, $field_map) + { + $dataFinal = []; + foreach ($field_map as $key => $value) { + $triggerValue = $value->formField; + $actionValue = $value->klaviyoFormField; + if ($triggerValue === 'custom') { + $dataFinal[$actionValue] = Common::replaceFieldWithValue($value->customValue, $data); + } elseif (!\is_null($data[$triggerValue])) { + $dataFinal[$actionValue] = $data[$triggerValue]; + } + } + + return $dataFinal; + } + + public function execute( + $listId, + $fieldValues, + $field_map, + $authKey + ) { + $typeName = 'add-members'; + $finalData = $this->generateReqDataFromFieldMap($fieldValues, $field_map); + $apiResponse = $this->handleProfile($authKey, $listId, $finalData, $fieldValues, $typeName); + + if (isset($apiResponse->errors)) { + $res = ['success' => false, 'message' => $apiResponse->errors[0]->detail, 'code' => 400]; + LogHandler::save($this->_integrationID, wp_json_encode(['type' => 'members', 'type_name' => $typeName]), 'error', wp_json_encode($res)); + } else { + $res = ['success' => true, 'message' => $apiResponse, 'code' => 200]; + LogHandler::save($this->_integrationID, wp_json_encode(['type' => 'members', 'type_name' => $typeName]), 'success', wp_json_encode($res)); + } + + return $apiResponse; + } + + private function handleProfile($authKey, $listId, $data, $fieldValues, &$typeName) + { + $id = $this->existProfile($data['email'], $authKey); + + $data = [ + 'data' => [ + 'type' => 'profile', + 'attributes' => $data + ] + ]; + + $data = Hooks::apply(Config::withPrefix('klaviyo_custom_properties'), $data, $this->_integrationDetails->custom_field_map ?? [], $fieldValues); + + /** + * @deprecated 2.7.8 Use `bit_integrations_klaviyo_custom_properties` filter instead. + * @since 2.7.8 + */ + $data = Hooks::apply('btcbi_klaviyo_custom_properties', $data, $this->_integrationDetails->custom_field_map ?? [], $fieldValues); + + if (empty($this->_integrationDetails->update) || empty($id)) { + return $this->createProfile($authKey, $listId, $data, $fieldValues); + } + + $typeName = 'update-members'; + $response = Hooks::apply(Config::withPrefix('klaviyo_update_profile'), false, $id, $authKey, $data); + + /** + * @deprecated 2.7.8 Use `bit_integrations_klaviyo_update_profile` filter instead. + * @since 2.7.8 + */ + $response = Hooks::apply('btcbi_klaviyo_update_profile', $response, $id, $authKey, $data); + + if (!$response) { + // translators: %s: Plugin name + return (object) ['errors' => [(object) ['detail' => wp_sprintf(__('%s plugin is not installed or activate', 'bit-integrations'), 'Bit Integrations Pro')]]]; + } + + return $response; + } + + private function existProfile($email, $authKey) + { + if (empty($email)) { + return false; + } + + $apiEndpoints = "https://a.klaviyo.com/api/profiles?filter=equals(email,'{$email}')"; + $apiResponse = HttpHelper::get($apiEndpoints, null, $this->setHeaders($authKey)); + + return $apiResponse->data[0]->id ?? false; + } + + private function createProfile($authKey, $listId, $data, $fieldValues) + { + $apiEndpoints = "{$this->baseUrl}profiles"; + $apiResponse = HttpHelper::post($apiEndpoints, wp_json_encode($data), $this->setHeaders($authKey)); + + if (!isset($apiResponse->data)) { + return $apiResponse; + } + + $data = [ + 'data' => [(object) [ + 'type' => 'profile', + 'id' => $apiResponse->data->id + ]] + ]; + + $apiEndpoints = "{$this->baseUrl}lists/{$listId}/relationships/profiles"; + + return HttpHelper::post($apiEndpoints, wp_json_encode($data), $this->setHeaders($authKey)); + } + + private function setHeaders($authKey) + { + return [ + 'Authorization' => "Klaviyo-API-Key {$authKey}", + 'Content-Type' => 'application/json', + 'accept' => 'application/json', + 'revision' => '2024-02-15' + ]; + } +} diff --git a/backend/Actions/Klaviyo/Routes.php b/backend/Actions/Klaviyo/Routes.php new file mode 100644 index 000000000..33c812ece --- /dev/null +++ b/backend/Actions/Klaviyo/Routes.php @@ -0,0 +1,10 @@ +module; if (empty($fieldMap) || empty($apiSecret) || empty($module) || empty($apiKey) || empty($baseUrl)) { + // translators: %s: Placeholder value return new WP_Error('REQ_FIELD_EMPTY', wp_sprintf(__('module, fields are required for %s api', 'bit-integrations'), 'License Manager For WooCommerce')); } diff --git a/backend/Actions/LMFWC/RecordApiHelper.php b/backend/Actions/LMFWC/RecordApiHelper.php new file mode 100644 index 000000000..c49f1aee7 --- /dev/null +++ b/backend/Actions/LMFWC/RecordApiHelper.php @@ -0,0 +1,301 @@ +integrationDetails = $integrationDetails; + $this->integrationId = $integId; + $this->apiUrl = "{$baseUrl}/wp-json/lmfwc/v2"; + $this->defaultHeader = [ + 'Authorization' => 'Basic ' . base64_encode("{$apiKey}:{$apiSecret}"), + 'Content-Type' => 'application/json', + ]; + } + + public function createLicense($finalData) + { + $this->type = 'Create license'; + $this->typeName = 'Create license'; + + if (empty($finalData['license_key'])) { + return ['success' => false, 'message' => __('Required field license key is empty', 'bit-integrations'), 'code' => 400]; + } + if (empty($this->integrationDetails->selectedStatus)) { + return ['success' => false, 'message' => __('Required field status is empty', 'bit-integrations'), 'code' => 400]; + } + + if (isset($this->integrationDetails->selectedStatus) || !empty($this->integrationDetails->selectedStatus)) { + $finalData['status'] = $this->integrationDetails->selectedStatus; + } + if (isset($this->integrationDetails->selectedCustomer) || !empty($this->integrationDetails->selectedCustomer)) { + $finalData['user_id'] = $this->integrationDetails->selectedCustomer; + } + if (isset($this->integrationDetails->selectedOrder) || !empty($this->integrationDetails->selectedOrder)) { + $finalData['order_id'] = $this->integrationDetails->selectedOrder; + } + if (isset($this->integrationDetails->selectedProduct) || !empty($this->integrationDetails->selectedProduct)) { + $finalData['product_id'] = $this->integrationDetails->selectedProduct; + } + + $apiEndpoint = $this->apiUrl . '/licenses'; + + return HttpHelper::post($apiEndpoint, wp_json_encode($finalData), $this->defaultHeader, ['sslverify' => false]); + } + + public function updateLicense($finalData) + { + $this->type = 'Update license'; + $this->typeName = 'Update license'; + + if (empty($this->integrationDetails->selectedLicense)) { + return ['success' => false, 'message' => __('Required field license is empty', 'bit-integrations'), 'code' => 400]; + } + + if (isset($this->integrationDetails->selectedStatus) || !empty($this->integrationDetails->selectedStatus)) { + $finalData['status'] = $this->integrationDetails->selectedStatus; + } + if (isset($this->integrationDetails->selectedCustomer) || !empty($this->integrationDetails->selectedCustomer)) { + $finalData['user_id'] = $this->integrationDetails->selectedCustomer; + } + if (isset($this->integrationDetails->selectedOrder) || !empty($this->integrationDetails->selectedOrder)) { + $finalData['order_id'] = $this->integrationDetails->selectedOrder; + } + if (isset($this->integrationDetails->selectedProduct) || !empty($this->integrationDetails->selectedProduct)) { + $finalData['product_id'] = $this->integrationDetails->selectedProduct; + } + + $response = Hooks::apply(Config::withPrefix('lmfwc_update_licence'), false, $finalData, $this->apiUrl, $this->integrationDetails, $this->defaultHeader); + + /** + * @deprecated 2.7.8 Use `bit_integrations_lmfwc_update_licence` filter instead. + * @since 2.7.8 + */ + $response = Hooks::apply('btcbi_lmfwc_update_licence', $response, $finalData, $this->apiUrl, $this->integrationDetails, $this->defaultHeader); + if (!$response) { + // translators: %s: Plugin name + return (object) ['message' => wp_sprintf(__('%s plugin is not installed or activate', 'bit-integrations'), 'Bit Integrations Pro')]; + } + + return $response; + } + + public function updateGenerator($finalData) + { + $this->type = 'Update generator'; + $this->typeName = 'Update generator'; + + if (empty($this->integrationDetails->selectedGenerator)) { + return ['success' => false, 'message' => __('Required field Generator is empty', 'bit-integrations'), 'code' => 400]; + } + + $response = Hooks::apply(Config::withPrefix('lmfwc_update_generator'), false, $this->apiUrl, $finalData, $this->defaultHeader, $this->integrationDetails->selectedGenerator); + + /** + * @deprecated 2.7.8 Use `bit_integrations_lmfwc_update_generator` filter instead. + * @since 2.7.8 + */ + $response = Hooks::apply('btcbi_lmfwc_update_generator', $response, $this->apiUrl, $finalData, $this->defaultHeader, $this->integrationDetails->selectedGenerator); + if (!$response) { + // translators: %s: Plugin name + return (object) ['message' => wp_sprintf(__('%s plugin is not installed or activate', 'bit-integrations'), 'Bit Integrations Pro')]; + } + + return $response; + } + + public function createGenerator($finalData) + { + $this->type = 'Create generator'; + $this->typeName = 'Create generator'; + + if (empty($finalData['name'])) { + return ['success' => false, 'message' => __('Required field name is empty', 'bit-integrations'), 'code' => 400]; + } + if (empty($finalData['charset'])) { + return ['success' => false, 'message' => __('Required field Character map is empty', 'bit-integrations'), 'code' => 400]; + } + if (empty($finalData['chunks'])) { + return ['success' => false, 'message' => __('Required field Number of chunks is empty', 'bit-integrations'), 'code' => 400]; + } + if (empty($finalData['chunk_length'])) { + return ['success' => false, 'message' => __('Required field Chunk length is empty', 'bit-integrations'), 'code' => 400]; + } + + $response = Hooks::apply(Config::withPrefix('lmfwc_create_generator'), false, $this->apiUrl, $finalData, $this->defaultHeader); + + /** + * @deprecated 2.7.8 Use `bit_integrations_lmfwc_create_generator` filter instead. + * @since 2.7.8 + */ + $response = Hooks::apply('btcbi_lmfwc_create_generator', $response, $this->apiUrl, $finalData, $this->defaultHeader); + if (!$response) { + // translators: %s: Plugin name + return (object) ['message' => wp_sprintf(__('%s plugin is not installed or activate', 'bit-integrations'), 'Bit Integrations Pro')]; + } + + return $response; + } + + public function licenseRelatedAction($finalData, $action) + { + $this->type = "{$action} license"; + $this->typeName = "{$action} license"; + + if (empty($finalData['license_key'])) { + return ['success' => false, 'message' => __('Required field license key is empty', 'bit-integrations'), 'code' => 400]; + } + + switch ($action) { + case 'activate': + $response = Hooks::apply(Config::withPrefix('lmfwc_activate_licence'), false, $this->apiUrl, $finalData['license_key'], $this->defaultHeader); + + /** + * @deprecated 2.7.8 Use `bit_integrations_lmfwc_activate_licence` filter instead. + * @since 2.7.8 + */ + $response = Hooks::apply('btcbi_lmfwc_activate_licence', $response, $this->apiUrl, $finalData['license_key'], $this->defaultHeader); + + break; + case 'deactivate': + $response = Hooks::apply(Config::withPrefix('lmfwc_deactivate_licence'), false, $this->apiUrl, $finalData['license_key'], $this->defaultHeader, $finalData['token']); + + /** + * @deprecated 2.7.8 Use `bit_integrations_lmfwc_deactivate_licence` filter instead. + * @since 2.7.8 + */ + $response = Hooks::apply('btcbi_lmfwc_deactivate_licence', $response, $this->apiUrl, $finalData['license_key'], $this->defaultHeader, $finalData['token']); + + break; + case 'reactivate': + $response = Hooks::apply(Config::withPrefix('lmfwc_reactivate_licence'), false, $this->apiUrl, $finalData['license_key'], $this->defaultHeader, $finalData['token']); + + /** + * @deprecated 2.7.8 Use `bit_integrations_lmfwc_reactivate_licence` filter instead. + * @since 2.7.8 + */ + $response = Hooks::apply('btcbi_lmfwc_reactivate_licence', $response, $this->apiUrl, $finalData['license_key'], $this->defaultHeader, $finalData['token']); + + break; + case 'delete': + $response = Hooks::apply(Config::withPrefix('lmfwc_delete_licence'), false, $this->apiUrl, $finalData['license_key'], $this->defaultHeader); + + /** + * @deprecated 2.7.8 Use `bit_integrations_lmfwc_delete_licence` filter instead. + * @since 2.7.8 + */ + $response = Hooks::apply('btcbi_lmfwc_delete_licence', $response, $this->apiUrl, $finalData['license_key'], $this->defaultHeader); + + break; + + default: + $response = false; + + break; + } + + if (!$response) { + // translators: %s: Plugin name + return (object) ['message' => wp_sprintf(__('%s plugin is not installed or activate', 'bit-integrations'), 'Bit Integrations Pro')]; + } + + return $response; + } + + public function generateReqDataFromFieldMap($data, $fieldMap) + { + $dataFinal = []; + foreach ($fieldMap as $value) { + $triggerValue = $value->formField; + $actionValue = $value->lmfwcFormField; + $dataFinal[$actionValue] = ($triggerValue === 'custom') ? Common::replaceFieldWithValue($value->customValue, $data) : $data[$triggerValue]; + } + + return $dataFinal; + } + + public function execute($fieldValues, $fieldMap, $module) + { + $finalData = $this->generateReqDataFromFieldMap($fieldValues, $fieldMap); + + switch ($module) { + case 'create_license': + $apiResponse = $this->createLicense($finalData); + + break; + case 'update_license': + $apiResponse = $this->updateLicense($finalData); + + break; + case 'activate_license': + $apiResponse = $this->licenseRelatedAction($finalData, 'activate'); + + break; + case 'deactivate_license': + $apiResponse = $this->licenseRelatedAction($finalData, 'deactivate'); + + break; + case 'reactivate_license': + $apiResponse = $this->licenseRelatedAction($finalData, 'reactivate'); + + break; + case 'delete_license': + $apiResponse = $this->licenseRelatedAction($finalData, 'delete'); + + break; + case 'create_generator': + $apiResponse = $this->createGenerator($finalData); + + break; + case 'update_generator': + $apiResponse = $this->updateGenerator($finalData); + + break; + } + + if (isset($apiResponse->success) && $apiResponse->success && !isset($apiResponse->data->errors)) { + $res = [$this->typeName . ' successfully']; + + LogHandler::save($this->integrationId, wp_json_encode(['type' => $this->type, 'type_name' => $this->typeName]), 'success', wp_json_encode($res)); + } else { + if (is_wp_error($apiResponse)) { + $res = $apiResponse->get_error_message(); + } elseif (isset($apiResponse->data->errors)) { + $res = $apiResponse->data->errors->lmfwc_rest_data_error[0] ?? wp_json_encode($apiResponse); + } else { + $res = !empty($apiResponse->message) ? $apiResponse->message : wp_json_encode($apiResponse); + } + + LogHandler::save($this->integrationId, wp_json_encode(['type' => $this->type, 'type_name' => $this->type . ' creating']), 'error', $res); + } + + return $apiResponse; + } +} diff --git a/backend/Actions/LMFWC/Routes.php b/backend/Actions/LMFWC/Routes.php new file mode 100644 index 000000000..d109e350c --- /dev/null +++ b/backend/Actions/LMFWC/Routes.php @@ -0,0 +1,15 @@ +_integrationDetails = $integrationDetails; + self::$integrationID = $integId; + } + + public static function getIntegrationId() + { + return $integrationID = self::$integrationID; + } + + public function getAssignmentList() + { + return $assignment_list = $this->assignment_list; + } + + public function generateReqDataFromFieldMap($data, $fieldMap) + { + $dataFinal = []; + + foreach ($fieldMap as $key => $value) { + $triggerValue = $value->formField; + $actionValue = $value->learnDeshFormField; + if ($triggerValue === 'custom') { + $dataFinal[$actionValue] = Common::replaceFieldWithValue($value->customValue, $data); + } elseif (!\is_null($data[$triggerValue])) { + $dataFinal[$actionValue] = $data[$triggerValue]; + } + } + + return $dataFinal; + } + + public static function createGroup( + $finalData, + $courseIds, + $userRole + ) { + $user_id = get_current_user_id(); + $group_title = $finalData['title']; + + $ld_group_args = [ + 'post_type' => 'groups', + // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- Using external plugin hook for compatibility + 'post_status' => apply_filters('uo_create_new_group_status', 'publish'), + 'post_title' => $group_title, + 'post_content' => '', + 'post_author' => $user_id, + ]; + + $group_id = wp_insert_post($ld_group_args); + if (is_wp_error($group_id)) { + return; + } + + $user = get_user_by('ID', $user_id); + + switch ($userRole) { + case '2': + $user->add_role('group_leader'); + + break; + case '3': + $user->set_role('group_leader'); + + break; + } + ld_update_leader_group_access($user_id, $group_id); + + $group_courses = explode(',', $courseIds); + + if (!empty($group_courses)) { + foreach ($group_courses as $course_id) { + ld_update_course_group_access((int) $course_id, (int) $group_id, false); + $transient_key = 'learndash_course_groups_' . $course_id; + delete_transient($transient_key); + } + } + + return $group_id; + } + + public static function enrollTheUserInACourse($courseIds) + { + $user_id = get_current_user_id(); + + if (!\function_exists('ld_update_course_access')) { + return __('The function ld_update_course_access does not exist', 'bit-integrations'); + } + + $course_id = $courseIds; + + return ld_update_course_access($user_id, $course_id); + } + + public static function makeThUserTheLeaderOfGroup($leaderRole, $leaderOfGroup) + { + $user_id = get_current_user_id(); + $user = get_user_by('ID', $user_id); + + if (is_wp_error($user)) { + return; + } + + if (user_can($user, 'group_leader')) { + ld_update_leader_group_access($user_id, $leaderOfGroup); + + return; + } + + switch ($leaderRole) { + case '2': + $user->add_role('group_leader'); + $apiResponse = ld_update_leader_group_access($user_id, $leaderOfGroup); + + break; + case '3': + $user->set_role('group_leader'); + $apiResponse = ld_update_leader_group_access($user_id, $leaderOfGroup); + + break; + } + + return $apiResponse; + } + + // Mark the course as complete for the user function 4. + public static function mark_quiz_complete($user_id, $course_id = null) + { + $quizzes = learndash_get_course_quiz_list($course_id, $user_id); + if ($quizzes) { + foreach ($quizzes as $quiz) { + $quiz_list[$quiz['post']->ID] = 0; + } + } + $quizz_progress = []; + if (!empty($quiz_list)) { + $usermeta = get_user_meta($user_id, '_sfwd-quizzes', true); + $quizz_progress = empty($usermeta) ? [] : $usermeta; + + foreach ($quiz_list as $quiz_id => $quiz) { + $quiz_meta = get_post_meta($quiz_id, '_sfwd-quiz', true); + + if (learndash_is_quiz_complete($user_id, $quiz_id, $course_id)) { + continue; + } + + $quizdata = [ + 'quiz' => $quiz_id, + 'score' => 0, + 'count' => 0, + 'pass' => true, + 'rank' => '-', + 'time' => time(), + 'pro_quizid' => $quiz_meta['sfwd-quiz_quiz_pro'], + 'course' => $course_id, + 'points' => 0, + 'total_points' => 0, + 'percentage' => 100, + 'timespent' => 0, + 'has_graded' => false, + 'statistic_ref_id' => 0, + 'm_edit_by' => 9999999, + 'm_edit_time' => time(), + ]; + + $quizz_progress[] = $quizdata; + + if ($quizdata['pass'] == true) { + $quizdata_pass = true; + } else { + $quizdata_pass = false; + } + + learndash_update_user_activity( + [ + 'course_id' => $course_id, + 'user_id' => $user_id, + 'post_id' => $quiz_id, + 'activity_type' => 'quiz', + 'activity_action' => 'insert', + 'activity_status' => true, + 'activity_started' => $quizdata['time'], + 'activity_completed' => $quizdata['time'], + 'activity_meta' => $quizdata, + ] + ); + } + } + + if (!empty($quizz_progress)) { + update_user_meta($user_id, '_sfwd-quizzes', $quizz_progress); + } + } + + // function 3. + public static function mark_topics_done($user_id, $lesson_id, $course_id) + { + $topic_list = learndash_get_topic_list($lesson_id, $course_id); + if ($topic_list) { + foreach ($topic_list as $topic) { + learndash_process_mark_complete($user_id, $topic->ID, false, $course_id); + $topic_quiz_list = learndash_get_lesson_quiz_list($topic->ID, $user_id, $course_id); + if ($topic_quiz_list) { + foreach ($topic_quiz_list as $ql) { + $quiz_list[$ql['post']->ID] = 0; + } + } + } + } + } + + // user function 2. + public static function mark_steps_done($user_id, $course_id) + { + $lessons = learndash_get_lesson_list($course_id, ['num' => 0]); + foreach ($lessons as $lesson) { + self::mark_topics_done($user_id, $lesson->ID, $course_id); + $lesson_quiz_list = learndash_get_lesson_quiz_list($lesson->ID, $user_id, $course_id); + + if ($lesson_quiz_list) { + foreach ($lesson_quiz_list as $ql) { + $quiz_list[$ql['post']->ID] = 0; + } + } + + learndash_process_mark_complete($user_id, $lesson->ID, false, $course_id); + } + + self::mark_quiz_complete($user_id, $course_id); + } + + // user function 1. + public static function markACourseCompleteForTheUser($courseIds) + { + $user_id = get_current_user_id(); + $course_id = $courseIds; + self::mark_steps_done($user_id, $course_id); + + return learndash_process_mark_complete($user_id, $course_id); + } + + // action 6 and 1st part + public static function courseLessonComplete( + $courseIds, + $lessonId + ) { + $user_id = get_current_user_id(); + + return self::mark_steps_done_for_six($user_id, $lessonId, $courseIds); + } + + public static function mark_steps_done_for_six($user_id, $lesson_id, $course_id) + { + $topic_list = learndash_get_topic_list($lesson_id, $course_id); + + if ($topic_list) { + foreach ($topic_list as $topic) { + $topic_quiz_list = learndash_get_lesson_quiz_list($topic->ID, $user_id, $course_id); + if ($topic_quiz_list) { + foreach ($topic_quiz_list as $ql) { + $quiz_list[$ql['post']->ID] = 0; + } + } + + self::mark_quiz_complete($user_id, $course_id); + + learndash_process_mark_complete($user_id, $topic->ID, false, $course_id); + } + } + + $lesson_quiz_list = learndash_get_lesson_quiz_list($lesson_id, $user_id, $course_id); + + if ($lesson_quiz_list) { + foreach ($lesson_quiz_list as $ql) { + self::$quiz_list[$ql['post']->ID] = 0; + } + } + + self::mark_quiz_complete($user_id, $course_id); + + return learndash_process_mark_complete($user_id, $lesson_id, false, $course_id); + } + + public static function topicComplete( + $course_id, + $lessonId, + $topic_id + ) { + $user_id = get_current_user_id(); + $topic_quiz_list = learndash_get_lesson_quiz_list($topic_id, $user_id, $course_id); + if ($topic_quiz_list) { + foreach ($topic_quiz_list as $ql) { + $quiz_list[$ql['post']->ID] = 0; + } + } + + self::mark_quiz_complete($user_id, $course_id); + + return learndash_process_mark_complete($user_id, $topic_id, false, $course_id); + } + + public static function addUserToGroup($group_id) + { + $user_id = get_current_user_id(); + $check_group = learndash_validate_groups([$group_id]); + if (empty($check_group)) { + LogHandler::save(self::getIntegrationId(), wp_json_encode(['type' => 'group', 'type_name' => 'Add-the-user-to-a-group']), 'error', wp_json_encode(__('Group not found', 'bit-integrations'))); + } + + return ld_update_group_access($user_id, $group_id); + } + + // action 7 starts here + public function courseLessonNotComplete( + $course_id, + $lesson_id + ) { + $user_id = get_current_user_id(); + + $this->mark_steps_incomplete($user_id, $lesson_id, $course_id); + + learndash_process_mark_incomplete($user_id, $course_id, $lesson_id, false); + } + + public function mark_steps_incomplete($user_id, $lesson_id, $course_id) + { + $topic_list = learndash_get_topic_list($lesson_id, $course_id); + + if ($topic_list) { + foreach ($topic_list as $topic) { + learndash_process_mark_incomplete($user_id, $course_id, $topic->ID, false); + $topic_quiz_list = learndash_get_lesson_quiz_list($topic->ID, $user_id, $course_id); + if ($topic_quiz_list) { + foreach ($topic_quiz_list as $ql) { + learndash_delete_quiz_progress($user_id, $ql['post']->ID); + } + } + } + } + + $lesson_quiz_list = learndash_get_lesson_quiz_list($lesson_id, $user_id, $course_id); + + if ($lesson_quiz_list) { + foreach ($lesson_quiz_list as $ql) { + learndash_delete_quiz_progress($user_id, $ql['post']->ID); + } + } + } + + public static function mark_quiz_incomplete($user_id, $course_id = null) + { + if (!empty($quiz_list)) { + foreach ($quiz_list as $quiz_id => $quiz) { + learndash_delete_quiz_progress($user_id, $quiz_id); + } + } + } + // action 7 ends here + + // action 9 starts here + + public static function topicNotComplete( + $course_id, + $lessonId, + $topic_id + ) { + // } + + // public function mark_not_complete_a_topic( $user_id, $action_data, $recipe_id, $args ) { + + $user_id = get_current_user_id(); + + $topic_quiz_list = learndash_get_lesson_quiz_list($topic_id, $user_id, $course_id); + if ($topic_quiz_list) { + foreach ($topic_quiz_list as $ql) { + $quiz_list[$ql['post']->ID] = 0; + } + } + + self::mark_quiz_incomplete($user_id, $course_id); + + return learndash_process_mark_incomplete($user_id, $course_id, $topic_id, false); + } + + // action 9 ends here + + // action 11 starts here + + public static function removeUserToGroup($group_id) + { + $user_id = get_current_user_id(); + if ('-1' !== $group_id) { + $apiResponse = ld_update_group_access($user_id, $group_id, true); + } else { + $all_groups_list = learndash_get_users_group_ids($user_id); + foreach ($all_groups_list as $group_id) { + $apiResponse = ld_update_group_access($user_id, $group_id, true); + } + } + + return $apiResponse; + } + + // action 11 ends here + + // action 13 starts here + + public function resetQuiz($quiz_id) + { + $user_id = get_current_user_id(); + + if ('-1' !== $quiz_id) { + self::delete_quiz_progress($user_id, $quiz_id); + } + } + + public static function delete_quiz_progress($user_id, $quiz_id = null) + { + global $wpdb; + + if (!empty($quiz_id)) { + $usermeta = get_user_meta($user_id, '_sfwd-quizzes', true); + $quizz_progress = empty($usermeta) ? [] : $usermeta; + foreach ($quizz_progress as $k => $p) { + if ((int) $p['quiz'] !== (int) $quiz_id) { + continue; + } + $statistic_ref_id = $p['statistic_ref_id']; + unset($quizz_progress[$k]); + if (!empty($statistic_ref_id)) { + if (class_exists('\LDLMS_DB')) { + $pro_quiz_stat_table = esc_sql(LDLMS_DB::get_table_name('quiz_statistic')); + $pro_quiz_stat_ref_table = esc_sql(LDLMS_DB::get_table_name('quiz_statistic_ref')); + } else { + $pro_quiz_stat_table = esc_sql($wpdb->prefix . 'wp_pro_quiz_statistic'); + $pro_quiz_stat_ref_table = esc_sql($wpdb->prefix . 'wp_pro_quiz_statistic_ref'); + } + + // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- Table name cannot be parameterized + $wpdb->query($wpdb->prepare("DELETE FROM {$pro_quiz_stat_table} WHERE statistic_ref_id = %d", $statistic_ref_id)); + // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- Table name cannot be parameterized + $wpdb->query($wpdb->prepare("DELETE FROM {$pro_quiz_stat_ref_table} WHERE statistic_ref_id = %d", $statistic_ref_id)); + } + } + $apiResponse = update_user_meta($user_id, '_sfwd-quizzes', $quizz_progress); + + return $apiResponse; + } + + return false; + } + + // action 13 ends here + + // action 17 starts here + + public function UnenrollUserFromCourse($course_id) + { + $user_id = 5; + + if ('any' === $course_id) { + $user_courses = learndash_user_get_enrolled_courses($user_id); + foreach ($user_courses as $course_id) { + $apiResponse = ld_update_course_access($user_id, $course_id, true); + } + } else { + $apiResponse = ld_update_course_access($user_id, $course_id, true); + } + + return $apiResponse; + } + + // action 17 ends here + + // action 10 starts here + + // public function remove_from_group( $user_id, $action_data, $recipe_id, $args ) + + public static function removeGroupLeaderAndChildren($group_id) + { + $user_id = get_current_user_id(); + if (!self::is_group_hierarchy_enabled()) { + $error_message = 'The LearnDash Group hierarchy setting is not enabled.'; + LogHandler::save(self::getIntegrationId(), wp_json_encode(['type' => 'group', 'type_name' => 'remove-leader-from-group']), 'error', wp_json_encode($error_message)); + + return; + } + + $all_hierarchy_groups = self::get_group_children_in_an_action($group_id, 1, []); + $all_hierarchy_groups[] = $group_id; + $all_groups_list = learndash_get_administrators_group_ids($user_id, true); + $common = array_intersect($all_hierarchy_groups, $all_groups_list); + + if (!$common) { + $error_message = 'The Group Leader is not an admin of any of the groups in hierarchy.'; + LogHandler::save(self::getIntegrationId(), wp_json_encode(['type' => 'group', 'type_name' => 'remove-leader-from-group']), 'error', wp_json_encode($error_message)); + + return; + } + foreach ($common as $group_id) { + $apiResponse = ld_update_leader_group_access($user_id, $group_id, true); + } + + return $apiResponse; + } + + public static function is_group_hierarchy_enabled() + { + $settings = get_option('learndash_settings_groups_management_display'); + if (empty($settings)) { + return false; + } + if (!isset($settings['group_hierarchical_enabled'])) { + return false; + } + + return ! ('yes' !== $settings['group_hierarchical_enabled']) + + ; + } + + public static function get_group_children_in_an_action($parent_id, $depth = 1, $groups = []) + { + $args = [ + 'post_type' => 'groups', + 'posts_per_page' => 9999, + 'orderby' => 'title', + 'order' => 'ASC', + 'post_status' => 'publish', + 'post_parent' => $parent_id, + ]; + $results = get_posts($args); + if ($results) { + foreach ($results as $r) { + $group_id = $r->ID; + $groups[] = $group_id; + $ld_children = learndash_get_group_children($group_id); + $groups = array_merge($groups, $ld_children); + self::get_group_children_in_an_action($r->ID, ++$depth, $groups); + } + } + if (empty($groups)) { + return []; + } + $ld_children = learndash_get_group_children($parent_id); + + return array_unique(array_merge($groups, $ld_children)); + } + + // action 10 ends here + + // action 12 starts here + public static function removeUserAndChildrenFromGroup($group_id) + { + $user_id = get_current_user_id(); + + if (!self::is_group_hierarchy_enabled()) { + $error_message = 'The LearnDash Group hierarchy setting is not enabled.'; + LogHandler::save(self::getIntegrationId(), wp_json_encode(['type' => 'group', 'type_name' => 'remove-user-from-group']), 'error', wp_json_encode($error_message)); + + return; + } + $all_hierarchy_groups = self::get_group_children_in_an_action($group_id, 1, []); + $all_hierarchy_groups[] = $group_id; + $all_current_user_groups = learndash_get_users_group_ids($user_id, true); + $common = array_intersect($all_hierarchy_groups, $all_current_user_groups); + if (!$common) { + $error_message = 'The user does not belong to any of the groups in the hierarchy.'; + LogHandler::save(self::getIntegrationId(), wp_json_encode(['type' => 'group', 'type_name' => 'remove-user-from-group']), 'error', wp_json_encode($error_message)); + + return; + } + foreach ($common as $group_id) { + $apiResponse = ld_update_group_access($user_id, $group_id, true); + } + + return $apiResponse; + } + + // action 12 ends here + + // action 14 starts here + + public static function resetUserProgressInCourse($course_id) + { + $user_id = get_current_user_id(); + $reset_tc_data = false; + + if ('-1' !== $course_id) { + self::delete_user_activity($user_id, $course_id); + if (self::delete_course_progress($user_id, $course_id)) { + self::reset_quiz_progress($user_id, $course_id); + self::delete_assignments(); + } + // } + $apiResponse = self::reset_quiz_progress($user_id, $course_id); + } + + return $apiResponse; + } + + public static function delete_user_activity($user_id, $course_id) + { + global $wpdb; + delete_user_meta($user_id, 'completed_' . $course_id); + delete_user_meta($user_id, 'course_completed_' . $course_id); + delete_user_meta($user_id, 'learndash_course_expired_' . $course_id); + + // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Direct query needed for LearnDash activity + $activity_ids = $wpdb->get_results($wpdb->prepare("SELECT activity_id FROM {$wpdb->prefix}learndash_user_activity WHERE course_id = %d AND user_id = %d", $course_id, $user_id)); + + if ($activity_ids) { + foreach ($activity_ids as $activity_id) { + // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching + $wpdb->query($wpdb->prepare("DELETE FROM {$wpdb->prefix}learndash_user_activity_meta WHERE activity_id = %d", $activity_id->activity_id)); + // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching + $wpdb->query($wpdb->prepare("DELETE FROM {$wpdb->prefix}learndash_user_activity WHERE activity_id = %d", $activity_id->activity_id)); + } + } + } + + public static function delete_course_progress($user_id, $course_id) + { + $usermeta = get_user_meta($user_id, '_sfwd-course_progress', true); + if (!empty($usermeta) && isset($usermeta[$course_id])) { + unset($usermeta[$course_id]); + update_user_meta($user_id, '_sfwd-course_progress', $usermeta); + + return true; + } + + return false; + } + + public static function reset_quiz_progress($user_id, $course_id) + { + $lessons = learndash_get_lesson_list($course_id, ['num' => 0]); + foreach ($lessons as $lesson) { + self::get_topics_quiz($user_id, $lesson->ID, $course_id); + $lesson_quiz_list = learndash_get_lesson_quiz_list($lesson->ID, $user_id, $course_id); + + if ($lesson_quiz_list) { + foreach ($lesson_quiz_list as $ql) { + $quiz_list[$ql['post']->ID] = 0; + } + } + + $assignments = get_posts([ + 'post_type' => 'sfwd-assignment', + 'posts_per_page' => 999, + // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query -- Required to match LearnDash assignment meta to course/lesson/user. + 'meta_query' => [ + 'relation' => 'AND', + [ + 'key' => 'lesson_id', + 'value' => $lesson->ID, + 'compare' => '=', + ], + [ + 'key' => 'course_id', + 'value' => $course_id, + 'compare' => '=', + ], + [ + 'key' => 'user_id', + 'value' => $user_id, + 'compare' => '=', + ], + ], + ]); + + if ($assignments) { + foreach ($assignments as $assignment) { + $assignment_list[] = $assignment->ID; + } + } + } + + self::delete_quiz_progress($user_id, $course_id); + } + + public static function get_topics_quiz($user_id, $lesson_id, $course_id) + { + $topic_list = learndash_get_topic_list($lesson_id, $course_id); + if ($topic_list) { + foreach ($topic_list as $topic) { + $topic_quiz_list = learndash_get_lesson_quiz_list($topic->ID, $user_id, $course_id); + if ($topic_quiz_list) { + foreach ($topic_quiz_list as $ql) { + $quiz_list[$ql['post']->ID] = 0; + } + } + + $assignments = get_posts([ + 'post_type' => 'sfwd-assignment', + 'posts_per_page' => 999, + // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query -- Required to match LearnDash topic assignment meta to course/topic/user. + 'meta_query' => [ + 'relation' => 'AND', + [ + 'key' => 'lesson_id', + 'value' => $topic->ID, + 'compare' => '=', + ], + [ + 'key' => 'course_id', + 'value' => $course_id, + 'compare' => '=', + ], + [ + 'key' => 'user_id', + 'value' => $user_id, + 'compare' => '=', + ], + ], + ]); + + if ($assignments) { + foreach ($assignments as $assignment) { + $assignment_list[] = $assignment->ID; + } + } + } + } + } + + public static function delete_assignments() + { + global $wpdb; + $assignments = self::getAssignmentList(); + if ($assignments) { + foreach ($assignments as $assignment) { + // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Direct queries needed for LearnDash assignments + $wpdb->query($wpdb->prepare("DELETE FROM {$wpdb->posts} WHERE ID = %d", $assignment)); + // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching + $wpdb->query($wpdb->prepare("DELETE FROM {$wpdb->postmeta} WHERE post_id = %d", $assignment)); + } + } + } + + // action 14 ends here + + // action 16 starts here + + public function sendMailToGroupLeader($integrationData, $fieldValues) + { + $mailInstance = new MailController(); + + return $mailInstance->execute($integrationData, $fieldValues); + } + + // action 16 ends here + + public function execute( + $mainAction, + $fieldValues, + $integrationDetails, + $integrationData + ) { + $fieldData = []; + if ($mainAction === '1') { + $userRole = $integrationDetails->userRole; + $fieldMap = $integrationDetails->field_map; + $finalData = $this->generateReqDataFromFieldMap($fieldValues, $fieldMap); + $courseIds = $integrationDetails->courseId; + $apiResponse = self::createGroup( + $finalData, + $courseIds, + $userRole + ); + if (is_wp_error($apiResponse)) { + $error_message = $apiResponse->get_error_message(); + LogHandler::save(self::$integrationID, wp_json_encode(['type' => 'group', 'type_name' => 'create-group']), 'error', wp_json_encode($error_message)); + } else { + LogHandler::save(self::$integrationID, wp_json_encode(['type' => 'group', 'type_name' => 'create-group']), 'success', wp_json_encode($apiResponse)); + } + } + + if ($mainAction === '2') { + $groupId = $integrationDetails->groupId; + $apiResponse = self::addUserToGroup($groupId); + if (is_wp_error($apiResponse)) { + $error_message = $apiResponse->get_error_message(); + LogHandler::save(self::$integrationID, wp_json_encode(['type' => 'group', 'type_name' => 'Add-the-user-to-a-group']), 'error', wp_json_encode($error_message)); + } else { + LogHandler::save(self::$integrationID, wp_json_encode(['type' => 'group', 'type_name' => 'Add-the-user-to-a-group']), 'success', wp_json_encode($apiResponse)); + } + } + + if ($mainAction === '3') { + $courseIds = $integrationDetails->courseId; + + $apiResponse = self::enrollTheUserInACourse($courseIds); + if (is_wp_error($apiResponse)) { + $error_message = $apiResponse->get_error_message(); + LogHandler::save(self::$integrationID, wp_json_encode(['type' => 'group', 'type_name' => 'enroll-user-in-course']), 'error', wp_json_encode($error_message)); + } else { + LogHandler::save(self::$integrationID, wp_json_encode(['type' => 'group', 'type_name' => 'enroll-user-in-course']), 'success', wp_json_encode($apiResponse)); + } + } + + if ($mainAction === '4') { + $leaderRole = $integrationDetails->leaderRole; + $leaderOfGroup = $integrationDetails->leaderOfGroup; + // $user + $apiResponse = self::makeThUserTheLeaderOfGroup($leaderRole, $leaderOfGroup); + if (is_wp_error($apiResponse)) { + $error_message = $apiResponse->get_error_message(); + LogHandler::save(self::$integrationID, wp_json_encode(['type' => 'group', 'type_name' => 'Make-the-user-the-leader-of-group']), 'error', wp_json_encode($error_message)); + } else { + LogHandler::save(self::$integrationID, wp_json_encode(['type' => 'group', 'type_name' => 'Make-the-user-the-leader-of-group']), 'success', wp_json_encode($apiResponse)); + } + } + if ($mainAction === '5') { + $courseIds = $integrationDetails->courseId; + $apiResponse = self::markACourseCompleteForTheUser($courseIds); + if (is_wp_error($apiResponse)) { + $error_message = $apiResponse->get_error_message(); + LogHandler::save(self::$integrationID, wp_json_encode(['type' => 'group', 'type_name' => 'Mark-a-course-complete-for-the-user']), 'error', wp_json_encode($error_message)); + } else { + LogHandler::save(self::$integrationID, wp_json_encode(['type' => 'group', 'type_name' => 'Mark-a-course-complete-for-the-user']), 'success', wp_json_encode($apiResponse)); + } + } + + if ($mainAction === '6') { + $courseIds = $integrationDetails->courseId; + $lessonId = $integrationDetails->lessonId; + $apiResponse = self::courseLessonComplete( + $courseIds, + $lessonId + ); + + if (is_wp_error($apiResponse)) { + $error_message = $apiResponse->get_error_message(); + LogHandler::save(self::$integrationID, wp_json_encode(['type' => 'group', 'type_name' => 'Mark-a-lesson-complete-for-the-user']), 'error', wp_json_encode($error_message)); + } else { + LogHandler::save(self::$integrationID, wp_json_encode(['type' => 'group', 'type_name' => 'Mark-a-lesson-complete-for-the-user']), 'success', wp_json_encode($apiResponse)); + } + } + if ($mainAction === '7') { + $courseIds = $integrationDetails->courseId; + $lessonId = $integrationDetails->lessonId; + $apiResponse = self::courseLessonNotComplete( + $courseIds, + $lessonId + ); + + if (is_wp_error($apiResponse)) { + $error_message = __('failed lesson not complete', 'bit-integrations'); + LogHandler::save(self::$integrationID, wp_json_encode(['type' => 'group', 'type_name' => 'Mark-a-lesson-complete-for-the-user']), 'error', wp_json_encode($error_message)); + } else { + LogHandler::save(self::$integrationID, wp_json_encode(['type' => 'group', 'type_name' => 'Mark-a-lesson-complete-for-the-user']), 'success', wp_json_encode($apiResponse)); + } + } + + if ($mainAction === '8') { + $courseIds = $integrationDetails->courseId; + $lessonId = $integrationDetails->lessonId; + $topicId = $integrationDetails->topicId; + $apiResponse = self::topicComplete( + $courseIds, + $lessonId, + $topicId + ); + if (is_wp_error($apiResponse)) { + $error_message = $apiResponse->get_error_message(); + LogHandler::save(self::$integrationID, wp_json_encode(['type' => 'group', 'type_name' => 'topic-complete-for-the-user']), 'error', wp_json_encode($error_message)); + } else { + LogHandler::save(self::$integrationID, wp_json_encode(['type' => 'group', 'type_name' => 'topic-complete-for-the-user']), 'success', wp_json_encode($apiResponse)); + } + } + if ($mainAction === '9') { + $courseIds = $integrationDetails->courseId; + $lessonId = $integrationDetails->lessonId; + $topicId = $integrationDetails->topicId; + $apiResponse = self::topicNotComplete( + $courseIds, + $lessonId, + $topicId + ); + if (is_wp_error($apiResponse)) { + $error_message = $apiResponse->get_error_message(); + LogHandler::save(self::$integrationID, wp_json_encode(['type' => 'group', 'type_name' => 'topic-not-complete-for-the-user']), 'error', wp_json_encode($error_message)); + } else { + LogHandler::save(self::$integrationID, wp_json_encode(['type' => 'group', 'type_name' => 'topic-not-complete-for-the-user']), 'success', wp_json_encode($apiResponse)); + } + } + + if ($mainAction === '10') { + $group_id = $integrationDetails->groupId10; + $apiResponse = self::removeGroupLeaderAndChildren($group_id); + if ($apiResponse) { + LogHandler::save(self::$integrationID, wp_json_encode(['type' => 'group', 'type_name' => 'Remove-Leader-from-group-and-its-children']), 'success', wp_json_encode(__('Remove Leader from group and its children successfully', 'bit-integrations'))); + } else { + LogHandler::save(self::$integrationID, wp_json_encode(['type' => 'group', 'type_name' => 'Remove-Leader-from-group-and-its-children']), 'error', wp_json_encode(__('Failed to remove leader from group and its children', 'bit-integrations'))); + } + } + + if ($mainAction === '11') { + $groupId = $integrationDetails->groupId11; + $apiResponse = self::removeUserToGroup($groupId); + if (is_wp_error($apiResponse)) { + LogHandler::save(self::$integrationID, wp_json_encode(['type' => 'group', 'type_name' => 'Remove-the-user-from-a-group']), 'error', wp_json_encode(__('Fail to remove user from group', 'bit-integrations'))); + } else { + LogHandler::save(self::$integrationID, wp_json_encode(['type' => 'group', 'type_name' => 'Remove-the-user-from-a-group']), 'success', wp_json_encode(__('User removed from group successfully', 'bit-integrations'))); + } + } + + if ($mainAction === '12') { + $group_id = $integrationDetails->groupId12; + $apiResponse = self::removeUserAndChildrenFromGroup($group_id); + if ($apiResponse) { + LogHandler::save(self::$integrationID, wp_json_encode(['type' => 'group', 'type_name' => 'Remove-user-from-group-and-its-children']), 'error', wp_json_encode(__('Remove user from group and its children successfully', 'bit-integrations'))); + } else { + LogHandler::save(self::$integrationID, wp_json_encode(['type' => 'group', 'type_name' => 'Remove-user-from-group-and-its-children']), 'success', wp_json_encode(__('Failed to remove user from group and its children', 'bit-integrations'))); + } + } + + if ($mainAction === '13') { + $quiz_id = $integrationDetails->quizId; + $apiResponse = self::resetQuiz($quiz_id); + if (is_wp_error($apiResponse)) { + LogHandler::save(self::$integrationID, wp_json_encode(['type' => 'quiz', 'type_name' => 'Reset-users-attempts-for-quiz']), 'error', wp_json_encode(__('Fail to reset quiz', 'bit-integrations'))); + } else { + LogHandler::save(self::$integrationID, wp_json_encode(['type' => 'quiz', 'type_name' => 'Reset-users-attempts-for-quiz']), 'success', wp_json_encode(__('Quiz reset successfully', 'bit-integrations'))); + } + } + + if ($mainAction === '14') { + $courseIds = $integrationDetails->courseId; + + $apiResponse = self::resetUserProgressInCourse($courseIds); + if (is_wp_error($apiResponse)) { + $error_message = $apiResponse->get_error_message(); + LogHandler::save(self::$integrationID, wp_json_encode(['type' => 'group', 'type_name' => 'enroll-user-in-course']), 'error', wp_json_encode($error_message)); + } else { + LogHandler::save(self::$integrationID, wp_json_encode(['type' => 'group', 'type_name' => 'enroll-user-in-course']), 'success', wp_json_encode($apiResponse)); + } + } + + if ($mainAction === '16') { + $apiResponse = self::sendMailToGroupLeader($integrationData, $fieldValues); + } + + if ($mainAction === '17') { + $course_id = $integrationDetails->courseId; + $apiResponse = self::UnenrollUserFromCourse($course_id); + if ($apiResponse) { + LogHandler::save(self::$integrationID, wp_json_encode(['type' => 'quiz', 'type_name' => 'users-unEnroll-course']), 'success', wp_json_encode(__('users-unenroll-course-successfully', 'bit-integrations'))); + } else { + LogHandler::save(self::$integrationID, wp_json_encode(['type' => 'quiz', 'type_name' => 'users-unEnroll-course']), 'error', wp_json_encode(__('users-unenroll-course-failed', 'bit-integrations'))); + } + } + + return $apiResponse; + } +} diff --git a/backend/Actions/LearnDash/Routes.php b/backend/Actions/LearnDash/Routes.php new file mode 100644 index 000000000..6bdc7fc26 --- /dev/null +++ b/backend/Actions/LearnDash/Routes.php @@ -0,0 +1,16 @@ +api_key; if (empty($fieldMap) || empty($apiKey) || empty($selectedCampaign)) { + // translators: %s: Placeholder value return new WP_Error('REQ_FIELD_EMPTY', wp_sprintf(__('module, fields are required for %s api', 'bit-integrations'), 'Lemlist')); } diff --git a/backend/Actions/Lemlist/RecordApiHelper.php b/backend/Actions/Lemlist/RecordApiHelper.php new file mode 100644 index 000000000..023c2ad02 --- /dev/null +++ b/backend/Actions/Lemlist/RecordApiHelper.php @@ -0,0 +1,97 @@ +_integrationDetails = $integrationDetails; + $this->_integrationID = $integId; + $this->_defaultHeader = [ + 'Authorization' => 'Basic ' . base64_encode(":{$apiKey}"), + 'Content-Type' => 'application/json' + ]; + } + + public function updateLead($email, $data, $selectedCampaign) + { + $contactData = $data; + $apiEndpoints = "https://api.lemlist.com/api/campaigns/{$selectedCampaign}/leads/{$email}"; + + return HttpHelper::request($apiEndpoints, 'PATCH', wp_json_encode($contactData), $this->_defaultHeader); + } + + public function addLead($selectedCampaign, $finalData) + { + $apiEndpoints = "https://api.lemlist.com/api/campaigns/{$selectedCampaign}/leads"; + + return HttpHelper::post($apiEndpoints, wp_json_encode($finalData), $this->_defaultHeader); + } + + public function generateReqDataFromFieldMap($data, $fieldMap) + { + $dataFinal = []; + foreach ($fieldMap as $value) { + $triggerValue = $value->formField; + $actionValue = $value->lemlistField; + + if ($triggerValue === 'custom') { + $dataFinal[$actionValue] = $value->customValue; + } elseif (!\is_null($data[$triggerValue])) { + $dataFinal[$actionValue] = $data[$triggerValue]; + } + } + + return $dataFinal; + } + + public function execute($selectedCampaign, $fieldValues, $fieldMap, $actions) + { + $finalData = (object) $this->generateReqDataFromFieldMap($fieldValues, $fieldMap); + $existLead = $this->existLead($selectedCampaign, $finalData->email); + + if (!$existLead->_id) { + $apiResponse = $this->addLead($selectedCampaign, $finalData); + + if ($apiResponse->_id) { + $res = ['message' => 'Lead added successfully']; + LogHandler::save($this->_integrationID, wp_json_encode(['type' => 'Lead', 'type_name' => 'Lead added']), 'success', wp_json_encode($res)); + } else { + LogHandler::save($this->_integrationID, wp_json_encode(['type' => 'Lead', 'type_name' => 'Adding Lead']), 'error', wp_json_encode($apiResponse)); + } + } else { + if ($actions->update) { + $apiResponse = $this->updateLead($existLead->email, $finalData, $selectedCampaign); + + if ($apiResponse->_id) { + $res = ['message' => 'Lead updated successfully']; + LogHandler::save($this->_integrationID, wp_json_encode(['type' => 'Lead', 'type_name' => 'Lead updated']), 'success', wp_json_encode($res)); + } else { + LogHandler::save($this->_integrationID, wp_json_encode(['type' => 'Lead', 'type_name' => 'updating Lead']), 'error', wp_json_encode($apiResponse)); + } + } else { + LogHandler::save($this->_integrationID, ['type' => 'Lead', 'type_name' => 'Adding Lead'], 'error', __('Email address already exists in the system', 'bit-integrations')); + wp_send_json_error(__('Email address already exists in the system', 'bit-integrations'), 400); + } + } + + return $apiResponse; + } + + private function existLead($selectedCampaign, $email) + { + $apiEndpoints = "https://api.lemlist.com/api/leads/{$email}?campaignId={$selectedCampaign}"; + + return HttpHelper::get($apiEndpoints, null, $this->_defaultHeader); + } +} diff --git a/backend/Actions/Lemlist/Routes.php b/backend/Actions/Lemlist/Routes.php new file mode 100644 index 000000000..2ac0857b1 --- /dev/null +++ b/backend/Actions/Lemlist/Routes.php @@ -0,0 +1,11 @@ + 'lesson', + 'posts_per_page' => 9999, + 'orderby' => 'title', + 'order' => 'ASC', + 'post_status' => 'publish', + ]; + + $lessonList = get_posts($lessonParams); + + foreach ($lessonList as $key => $val) { + $allLesson[] = [ + 'lesson_id' => $val->ID, + 'lesson_title' => $val->post_title, + ]; + } + + return $allLesson; + } + + public static function getAllSection() + { + $sectionParams = [ + 'post_type' => 'section', + 'posts_per_page' => 9999, + 'orderby' => 'title', + 'order' => 'ASC', + 'post_status' => 'publish', + ]; + + $sectionList = get_posts($sectionParams); + + foreach ($sectionList as $key => $val) { + $allSection[] = [ + 'section_id' => $val->ID, + 'section_title' => $val->post_title, + ]; + } + + return $allSection; + } + + public static function getAllLifterLmsCourse() + { + global $wpdb; + $cache_key = Config::withPrefix('lifterlms_courses'); + $cache_group = Config::VAR_PREFIX; + $allCourse = wp_cache_get($cache_key, $cache_group); + + if (false !== $allCourse) { + return $allCourse; + } + + $posts_table = esc_sql($wpdb->posts); + + // phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.PreparedSQL.NotPrepared + $allCourse = $wpdb->get_results( + 'SELECT ID, post_title FROM ' . $posts_table . ' WHERE ' . $posts_table . ".post_status = 'publish' AND " . $posts_table . ".post_type = 'course' ORDER BY post_title" + ); + // phpcs:enable + + wp_cache_set($cache_key, $allCourse, $cache_group, 10 * MINUTE_IN_SECONDS); + + return $allCourse; + } + + public static function getAllLifterLmsMembership() + { + global $wpdb; + $cache_key = Config::withPrefix('lifterlms_memberships'); + $cache_group = Config::VAR_PREFIX; + $allMembership = wp_cache_get($cache_key, $cache_group); + + if (false !== $allMembership) { + return $allMembership; + } + + $posts_table = esc_sql($wpdb->posts); + + // phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.PreparedSQL.NotPrepared + $allMembership = $wpdb->get_results( + 'SELECT ID, post_title FROM ' . $posts_table . ' WHERE ' . $posts_table . ".post_status = 'publish' AND " . $posts_table . ".post_type = 'llms_membership' ORDER BY post_title" + ); + // phpcs:enable + + wp_cache_set($cache_key, $allMembership, $cache_group, 10 * MINUTE_IN_SECONDS); + + return $allMembership; + } + + public function execute($integrationData, $fieldValues) + { + $integrationDetails = $integrationData->flow_details; + $integId = $integrationData->id; + $mainAction = $integrationDetails->mainAction; + if ( + empty($integId) + || empty($mainAction) + ) { + // translators: %s: Integration name + return new WP_Error('REQ_FIELD_EMPTY', wp_sprintf(__('Some important info are missing those are required for %s', 'bit-integrations'), 'LifterLms')); + } + $recordApiHelper = new RecordApiHelper($integrationDetails, $integId); + $lifterLmsApiResponse = $recordApiHelper->execute( + $mainAction, + $fieldValues, + $integrationDetails, + $integrationData + ); + + if (is_wp_error($lifterLmsApiResponse)) { + return $lifterLmsApiResponse; + } + + return $lifterLmsApiResponse; + } +} diff --git a/backend/Actions/LifterLms/RecordApiHelper.php b/backend/Actions/LifterLms/RecordApiHelper.php new file mode 100644 index 000000000..ff54b9327 --- /dev/null +++ b/backend/Actions/LifterLms/RecordApiHelper.php @@ -0,0 +1,216 @@ +_integrationDetails = $integrationDetails; + $this->integrationID = $integId; + } + + public function complete_lesson($lessonId) + { + $user_id = get_current_user_id(); + if (empty($user_id)) { + return new WP_Error('REQ_FIELD_EMPTY', __('User not logged in', 'bit-integrations')); + } + if (!\function_exists('llms_mark_complete')) { + return false; + } + + return llms_mark_complete($user_id, $lessonId, 'lesson'); + } + + public function complete_section($sectionId) + { + $user_id = get_current_user_id(); + if (empty($user_id)) { + return new WP_Error('REQ_FIELD_EMPTY', __('User not logged in', 'bit-integrations')); + } + if (!\function_exists('llms_mark_complete')) { + return false; + } + + $section = new LLMS_Section($sectionId); + $lessons = $section->get_lessons(); + if (!empty($lessons)) { + foreach ($lessons as $lesson) { + llms_mark_complete($user_id, $lesson->id, 'lesson'); + } + } + + return llms_mark_complete($user_id, $sectionId, 'section'); + } + + public function enrollIntoCourse($courseId) + { + $user_id = get_current_user_id(); + if (empty($user_id)) { + return new WP_Error('REQ_FIELD_EMPTY', __('User not logged in', 'bit-integrations')); + } + if (!\function_exists('llms_enroll_student')) { + return false; + } + + return llms_enroll_student($user_id, $courseId); + } + + public function markCompleteCourse($courseId) + { + $user_id = get_current_user_id(); + if (empty($user_id)) { + return new WP_Error('REQ_FIELD_EMPTY', __('User not logged in', 'bit-integrations')); + } + if (!\function_exists('llms_mark_complete')) { + return false; + } + + $course_id = $courseId; + $course = new LLMS_Course($course_id); + $sections = $course->get_sections(); + + if (!empty($sections)) { + foreach ($sections as $section) { + $section = new LLMS_Section($section->id); + $lessons = $section->get_lessons(); + if (!empty($lessons)) { + foreach ($lessons as $lesson) { + llms_mark_complete($user_id, $lesson->id, 'lesson'); + } + } + llms_mark_complete($user_id, $section->id, 'section'); + } + } + + return llms_mark_complete($user_id, $course_id, 'course'); + } + + public function unEnrollUserFromCourse($courseId) + { + $user_id = get_current_user_id(); + if (empty($user_id)) { + return new WP_Error('REQ_FIELD_EMPTY', __('User not logged in', 'bit-integrations')); + } + if (!\function_exists('llms_unenroll_student')) { + return false; + } + + return llms_unenroll_student($user_id, $courseId); + } + + public function enrollIntoMembership($membershipId) + { + $user_id = get_current_user_id(); + if (empty($user_id)) { + return new WP_Error('REQ_FIELD_EMPTY', __('User not logged in', 'bit-integrations')); + } + if (!\function_exists('llms_enroll_student')) { + return false; + } + + return llms_enroll_student($user_id, $membershipId); + } + + public function unEnrollUserFromMembership($membershipId) + { + $user_id = 30; + if (! \function_exists('llms_unenroll_student') && empty($user_id) && empty($membershipId)) { + return false; + } + + if ('All' === \intval($membershipId)) { + $student = llms_get_student($user_id); + $memberships = $student->get_memberships(['limit' => 999]); + if (isset($memberships['results']) && ! empty($memberships['results'])) { + foreach ($memberships['results'] as $membership) { + llms_unenroll_student($user_id, $membership, 'expired'); + } + + return true; + } + } else { + return llms_unenroll_student($user_id, $membershipId, 'expired'); + } + } + + public function execute( + $mainAction, + $fieldValues, + $integrationDetails, + $integrationData + ) { + $response = []; + $fieldData = []; + + if ($mainAction == 1) { + $lessonId = $integrationDetails->lessonId; + $response = $this->complete_lesson($lessonId); + if ($response) { + LogHandler::save($this->integrationID, wp_json_encode(['type' => 'lesson-complete', 'type_name' => 'user-lesson-complete']), 'success', __('Lesson completed successfully', 'bit-integrations')); + } else { + LogHandler::save($this->integrationID, wp_json_encode(['type' => 'lesson-complete', 'type_name' => 'user-lesson-complete']), 'error', __('Failed to completed lesson', 'bit-integrations')); + } + } elseif ($mainAction == 2) { + $sectionId = $integrationDetails->sectionId; + $response = $this->complete_section($sectionId); + if ($response) { + LogHandler::save($this->integrationID, wp_json_encode(['type' => 'section-complete', 'type_name' => 'user-section-complete']), 'success', __('section completed successfully.', 'bit-integrations')); + } else { + LogHandler::save($this->integrationID, wp_json_encode(['type' => 'section-complete', 'type_name' => 'user-section-complete']), 'error', __('Failed to completed section.', 'bit-integrations')); + } + } elseif ($mainAction == 3) { + $courseId = $integrationDetails->courseId; + $response = $this->enrollIntoCourse($courseId); + if ($response) { + LogHandler::save($this->integrationID, wp_json_encode(['type' => 'course-enroll', 'type_name' => 'user-course-enroll']), 'success', __('User enrolled into course successfully.', 'bit-integrations')); + } else { + LogHandler::save($this->integrationID, wp_json_encode(['type' => 'course-enroll', 'type_name' => 'user-course-enroll']), 'error', __('Failed to enroll user into course.', 'bit-integrations')); + } + } elseif ($mainAction == 4) { + $membershipId = $integrationDetails->membershipId; + $response = $this->enrollIntoMembership($membershipId); + if ($response) { + LogHandler::save($this->integrationID, wp_json_encode(['type' => 'membership-enroll', 'type_name' => 'user-membership-enroll']), 'success', __('User enrolled into membership successfully.', 'bit-integrations')); + } else { + LogHandler::save($this->integrationID, wp_json_encode(['type' => 'membership-enroll', 'type_name' => 'user-membership-enroll']), 'error', __('Failed to enroll user into membership.', 'bit-integrations')); + } + } elseif ($mainAction == 5) { + $courseId = $integrationDetails->courseId; + $response = $this->markCompleteCourse($courseId); + if ($response) { + LogHandler::save($this->integrationID, wp_json_encode(['type' => 'course-complete', 'type_name' => 'user-course-complete']), 'success', __('User completed course successfully.', 'bit-integrations')); + } else { + LogHandler::save($this->integrationID, wp_json_encode(['type' => 'course-complete', 'type_name' => 'user-course-complete']), 'error', __('Failed to complete course.', 'bit-integrations')); + } + } elseif ($mainAction == 6) { + $courseId = $integrationDetails->courseId; + $response = $this->unEnrollUserFromCourse($courseId); + if ($response) { + LogHandler::save($this->integrationID, wp_json_encode(['type' => 'course-unenroll', 'type_name' => 'user-course-unenroll']), 'success', __('User unenrolled from course successfully.', 'bit-integrations')); + } else { + LogHandler::save($this->integrationID, wp_json_encode(['type' => 'course-unenroll', 'type_name' => 'user-course-unenroll']), 'error', __('Failed to unenroll user from course.', 'bit-integrations')); + } + } elseif ($mainAction == 7) { + $membershipId = $integrationDetails->membershipId; + $response = $this->unEnrollUserFromMembership($membershipId); + if ($response) { + LogHandler::save($this->integrationID, wp_json_encode(['type' => 'membership-unenroll', 'type_name' => 'user-membership-unenroll']), 'success', __('User unenrolled from membership successfully.', 'bit-integrations')); + } else { + LogHandler::save($this->integrationID, wp_json_encode(['type' => 'membership-unenroll', 'type_name' => 'user-membership-unenroll']), 'error', __('Failed to unenroll user from membership.', 'bit-integrations')); + } + } + + return $response; + } +} diff --git a/backend/Actions/LifterLms/Routes.php b/backend/Actions/LifterLms/Routes.php new file mode 100644 index 000000000..7e7ef7eb6 --- /dev/null +++ b/backend/Actions/LifterLms/Routes.php @@ -0,0 +1,14 @@ +apiEndPoint = $apiEndPoint; + $this->accessToken = $accessToken; + $this->integrationID = $integrationID; + } + + public function execute($integrationDetails, $fieldValues) + { + $messages = $this->buildMessages($integrationDetails, $fieldValues); + + if (!isset($messages) || \count($messages) === 0) { + return ['error' => 'No valid message generated.']; + } + + $data = ['messages' => $messages]; + $type = $integrationDetails->messageType ?? 'sendPushMessage'; + + switch ($type) { + case 'sendReplyMessage': + $data['replyToken'] = $integrationDetails->replyToken ?? ''; + $response = $this->sendReplyMessage(json_encode($data)); + + break; + + case 'sendBroadcastMessage': + $response = $this->sendBroadcastMessage(json_encode($data)); + + break; + + default: + $data['to'] = $integrationDetails->recipientId ?? ''; + $response = $this->sendPushMessage(json_encode($data)); + $response = HttpHelper::$responseCode === 200 ? 'Push message sent successfully' : 'Failed'; + } + + $status = HttpHelper::$responseCode == 200 ? 'success' : 'error'; + + LogHandler::save($this->integrationID, ['type' => 'record', 'type_name' => $type], $status, $response); + + return $response; + } + + private function buildMessages($details, $values) + { + $messages = []; + + $text = $this->buildTextMessage($details, $values); + if ($text) { + $messages[] = $text; + } + + if (isset($details->sendSticker, $details->sticker_field_map)) { + $stickerRequiredFields = ['sticker_id', 'package_id']; + $stickers = $this->processGrouped( + $values, + $details->sticker_field_map, + $stickerRequiredFields, + [$this, 'transformSticker'] + ); + $messages = array_merge($messages, $stickers); + } + + if (isset($details->sendImage, $details->image_field_map)) { + $imageRequiredFields = ['originalContentUrl']; + $images = $this->processGrouped( + $values, + $details->image_field_map, + $imageRequiredFields, + [$this, 'transformImage'] + ); + $messages = array_merge($messages, $images); + } + + if (isset($details->sendAudio, $details->audio_field_map)) { + $audioFields = array_filter($details->audio_field_map, function ($field) { + return $field->fieldType === 'audio'; + }); + if (\count($audioFields) > 0) { + $audioRequiredFields = ['originalContentUrl']; + $audios = $this->processGrouped( + $values, + $audioFields, + $audioRequiredFields, + [$this, 'transformAudio'] + ); + $audios = array_filter($audios); + $messages = array_merge($messages, $audios); + } + } + + if (isset($details->sendVideo, $details->video_field_map)) { + $videoRequiredFields = ['originalContentUrl']; + $videos = $this->processGrouped( + $values, + $details->video_field_map, + $videoRequiredFields, + [$this, 'transformVideo'] + ); + $messages = array_merge($messages, $videos); + } + + if (isset($details->sendLocation, $details->location_field_map)) { + $locationRequiredFields = ['title', 'address', 'latitude', 'longitude']; + $locations = $this->processGrouped( + $values, + $details->location_field_map, + $locationRequiredFields, + [$this, 'transformLocation'] + ); + $messages = array_merge($messages, $locations); + } + + return array_values(array_filter($messages)); + } + + private function buildTextMessage($details, $values): ?array + { + $messageText = ''; + + if (isset($details->message_field_map)) { + $mappedMessage = $this->mapFields($values, $details->message_field_map); + if (isset($mappedMessage['message'])) { + $messageText = $mappedMessage['message']; + } + } + + if (!isset($messageText) && isset($details->message)) { + $messageText = $details->message; + } + + if (!isset($messageText)) { + return null; + } + + $message = ['type' => 'text', 'text' => $messageText]; + + if (isset($details->sendEmojis, $details->emojis_field_map)) { + $emojis = []; + $groups = $this->organizeFieldsByGroup($details->emojis_field_map); + foreach ($groups as $groupId => $groupFields) { + $emoji = $this->mapFields($values, $groupFields); + if (isset($emoji['emojis_id'], $emoji['product_id'], $emoji['index'])) { + $emojis[] = [ + 'index' => (int) $emoji['index'], + 'productId' => $emoji['product_id'], + 'emojiId' => $emoji['emojis_id'], + ]; + } + } + if (isset($emojis) && \count($emojis) > 0) { + $message['emojis'] = $emojis; + } + } + + return $message; + } + + private function organizeFieldsByGroup(array $fieldMap): array + { + $grouped = []; + foreach ($fieldMap as $field) { + $groupId = $field->groupId ?? 'default'; + if (!isset($grouped[$groupId])) { + $grouped[$groupId] = []; + } + $grouped[$groupId][] = $field; + } + + return $grouped; + } + + private function processGrouped(array $values, array $fieldMap, array $requiredKeys, callable $builder): array + { + $results = []; + $groups = $this->organizeFieldsByGroup($fieldMap); + foreach ($groups as $groupId => $groupFields) { + $data = $this->mapFields($values, $groupFields); + + $allPresent = true; + foreach ($requiredKeys as $key) { + if (!isset($data[$key])) { + $allPresent = false; + + break; + } + } + + if ($allPresent) { + $results[] = $builder($data); + } + } + + return $results; + } + + private function mapFields(array $data, array $fieldMap): array + { + $result = []; + + foreach ($fieldMap as $index => $field) { + $key = $field->lineFormField; + + if ($field->formField === 'custom' && (isset($field->customValue) && $field->customValue !== '')) { + $result[$key] = Common::replaceFieldWithValue($field->customValue, $data); + } elseif (isset($data[$field->formField])) { + $result[$key] = $data[$field->formField]; + } + } + + return $result; + } + + private function handleFilterResponse($response) + { + if (!isset($response)) { + // translators: %s: Placeholder value + return (object) ['error' => \wp_sprintf(\__('%s plugin is not installed or activated', 'bit-integrations'), 'Bit Integrations Pro')]; + } + + return $response; + } + + private function setHeaders(): array + { + return [ + 'Authorization' => 'Bearer ' . $this->accessToken, + 'Content-Type' => 'application/json' + ]; + } + + private function sendPushMessage($data) + { + return HttpHelper::post($this->apiEndPoint . '/message/push', $data, $this->setHeaders()); + } + + private function sendReplyMessage($data) + { + $response = Hooks::apply(Config::withPrefix('line_reply_message'), false, $data, $this->setHeaders(), $this->apiEndPoint); + + /** + * @deprecated 2.7.8 Use `bit_integrations_line_reply_message` filter instead. + * @since 2.7.8 + */ + $response = Hooks::apply('btcbi_line_reply_message', $response, $data, $this->setHeaders(), $this->apiEndPoint); + + return static::handleFilterResponse($response); + } + + private function sendBroadcastMessage($data) + { + $response = Hooks::apply(Config::withPrefix('line_broadcast_message'), false, $data, $this->setHeaders(), $this->apiEndPoint); + + /** + * @deprecated 2.7.8 Use `bit_integrations_line_broadcast_message` filter instead. + * @since 2.7.8 + */ + $response = Hooks::apply('btcbi_line_broadcast_message', $response, $data, $this->setHeaders(), $this->apiEndPoint); + + return static::handleFilterResponse($response); + } + + private function transformSticker(array $data): array + { + return [ + 'type' => 'sticker', + 'packageId' => $data['package_id'], + 'stickerId' => $data['sticker_id'], + ]; + } + + private function transformImage(array $data): array + { + return array_filter([ + 'type' => 'image', + 'originalContentUrl' => $data['originalContentUrl'], + 'previewImageUrl' => $data['previewImageUrl'] ?? null + ]); + } + + private function transformAudio(array $data): ?array + { + if (!isset($data['duration'])) { + return null; + } + + return [ + 'type' => 'audio', + 'originalContentUrl' => $data['originalContentUrl'], + 'duration' => $data['duration'] + ]; + } + + private function transformVideo(array $data): array + { + return array_filter([ + 'type' => 'video', + 'originalContentUrl' => $data['originalContentUrl'], + 'previewImageUrl' => $data['previewImageUrl'] ?? null + ]); + } + + private function transformLocation(array $data): array + { + return [ + 'type' => 'location', + 'title' => $data['title'], + 'address' => $data['address'], + 'latitude' => (float) $data['latitude'], + 'longitude' => (float) $data['longitude'], + ]; + } +} diff --git a/backend/Actions/Line/Routes.php b/backend/Actions/Line/Routes.php new file mode 100644 index 000000000..b4beb193d --- /dev/null +++ b/backend/Actions/Line/Routes.php @@ -0,0 +1,10 @@ +actionName; if (empty($fieldMap) || empty($tokenDetails) || 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')); } diff --git a/backend/Actions/LionDesk/RecordApiHelper.php b/backend/Actions/LionDesk/RecordApiHelper.php new file mode 100644 index 000000000..4a22578e4 --- /dev/null +++ b/backend/Actions/LionDesk/RecordApiHelper.php @@ -0,0 +1,134 @@ +integrationDetails = $integrationDetails; + $this->integrationId = $integId; + $this->tokenDetails = $tokenDetails; + $this->apiUrl = 'https://api-v2.liondesk.com/'; + $this->defaultHeader = [ + 'Authorization' => "Bearer {$tokenDetails->access_token}", + 'Content-Type' => 'application/json' + ]; + } + + public function addCampaign($finalData) + { + if (empty($finalData['name'])) { + return ['success' => false, 'message' => __('Required field Name is empty', 'bit-integrations'), 'code' => 400]; + } + + $this->type = 'Campaign'; + $this->typeName = 'Campaign created'; + $apiEndpoint = $this->apiUrl . '/campaigns'; + + return HttpHelper::post($apiEndpoint, wp_json_encode($finalData), $this->defaultHeader); + } + + public function addContact($finalData) + { + if (empty($finalData['email'])) { + return ['success' => false, 'message' => __('Required field Email is empty', 'bit-integrations'), 'code' => 400]; + } + + $staticFieldKeys = ['first_name', 'last_name', 'email', 'mobile_phone', 'home_phone', 'office_phone', 'fax', 'company', 'birthday', 'anniversary', 'spouse_name', 'spouse_email', 'spouse_phone', 'spouse_birthday', 'type', 'street_address_1', 'street_address_2', 'zip', 'city', 'state']; + $address = []; + $requestParams = []; + $customParams = []; + + foreach ($finalData as $key => $value) { + if (\in_array($key, $staticFieldKeys)) { + if ($key === 'type' || $key === 'street_address_1' || $key === 'street_address_2' || $key === 'zip' || $key === 'city' || $key === 'state') { + $address[$key] = $value; + } else { + $requestParams[$key] = $value; + } + } else { + $customParams[] + = (object) [ + 'id' => $key, + 'value' => $value + ] + ; + } + } + if (isset($this->integrationDetails->selectedTag) && !empty($this->integrationDetails->selectedTag)) { + $requestParams['tags'] = ($this->integrationDetails->selectedTag); + } + if (\count($customParams)) { + $requestParams['custom_fields'] = $customParams; + } + + $this->type = 'Contact'; + $this->typeName = 'Contact created'; + $apiEndpoint = $this->apiUrl . '/contacts'; + $apiResponse = HttpHelper::post($apiEndpoint, wp_json_encode($requestParams), $this->defaultHeader); + + if (isset($apiResponse->id) && \count($address) > 0) { + $apiEndpoint = $this->apiUrl . "/contacts/{$apiResponse->id}/addresses"; + + return HttpHelper::post($apiEndpoint, wp_json_encode($address), $this->defaultHeader); + } + + return $apiResponse; + } + + public function generateReqDataFromFieldMap($data, $fieldMap) + { + $dataFinal = []; + foreach ($fieldMap as $value) { + $triggerValue = $value->formField; + $actionValue = $value->lionDeskFormField; + $dataFinal[$actionValue] = ($triggerValue === 'custom') ? $value->customValue : $data[$triggerValue]; + } + + return $dataFinal; + } + + public function execute($fieldValues, $fieldMap, $actionName) + { + $finalData = $this->generateReqDataFromFieldMap($fieldValues, $fieldMap); + if ($actionName === 'campaign') { + $apiResponse = $this->addCampaign($finalData); + } else { + $apiResponse = $this->addContact($finalData); + } + + if (isset($apiResponse->id)) { + $res = [$this->typeName . ' successfully']; + LogHandler::save($this->integrationId, wp_json_encode(['type' => $this->type, 'type_name' => $this->typeName]), 'success', wp_json_encode($res)); + } else { + LogHandler::save($this->integrationId, wp_json_encode(['type' => $this->type, 'type_name' => $this->type . ' creating']), 'error', wp_json_encode($apiResponse->message)); + } + + return $apiResponse; + } +} diff --git a/backend/Actions/LionDesk/Routes.php b/backend/Actions/LionDesk/Routes.php new file mode 100644 index 000000000..19c5bf677 --- /dev/null +++ b/backend/Actions/LionDesk/Routes.php @@ -0,0 +1,12 @@ +field_map; if (empty($fieldMap) || empty($apiKey)) { + // translators: %s: Placeholder value return new WP_Error('REQ_FIELD_EMPTY', wp_sprintf(__('module, fields are required for %s api', 'bit-integrations'), 'Livestorm')); } diff --git a/backend/Actions/Livestorm/RecordApiHelper.php b/backend/Actions/Livestorm/RecordApiHelper.php new file mode 100644 index 000000000..f2e74f8e3 --- /dev/null +++ b/backend/Actions/Livestorm/RecordApiHelper.php @@ -0,0 +1,101 @@ +integrationDetails = $integrationDetails; + $this->integrationId = $integId; + $this->apiUrl = 'https://api.livestorm.co/v1'; + $this->defaultHeader = [ + 'Authorization' => $apiKey, + 'Accept' => 'application/json', + 'Content-Type' => 'application/json' + ]; + } + + public function registration($finalData) + { + $this->type = 'Add People to Event Session'; + $this->typeName = 'Add People to Event Session'; + + if (!isset($this->integrationDetails->selectedEvent) || empty($this->integrationDetails->selectedEvent)) { + // translators: %s: Field name + // translators: %s: Placeholder value + return ['success' => false, 'message' => wp_sprintf(__('Required field %s is empty', 'bit-integrations'), __('Event', 'bit-integrations')), 'code' => 400]; + } + if (!isset($this->integrationDetails->selectedSession) || empty($this->integrationDetails->selectedSession)) { + // translators: %s: Field name + // translators: %s: Placeholder value + return ['success' => false, 'message' => wp_sprintf(__('Required field %s is empty', 'bit-integrations'), __('Session', 'bit-integrations')), 'code' => 400]; + } + + $apiEndpoint = $this->apiUrl . "/sessions/{$this->integrationDetails->selectedSession}/people"; + + return HttpHelper::post($apiEndpoint, wp_json_encode($finalData), $this->defaultHeader); + } + + public function generateReqDataFromFieldMap($data, $fieldMap) + { + $fieldData = []; + foreach ($fieldMap as $value) { + $triggerValue = $value->formField; + $actionValue = $value->livestormFormField; + + $fieldData[] + = (object) [ + 'id' => $actionValue, + 'value' => $triggerValue === 'custom' ? $value->customValue : $data[$triggerValue] + ] + ; + } + + return [ + 'data' => [ + 'type' => 'people', + 'attributes' => [ + 'fields' => $fieldData + ] + ] + ]; + } + + public function execute($fieldValues, $fieldMap) + { + $finalData = $this->generateReqDataFromFieldMap($fieldValues, $fieldMap); + $apiResponse = $this->registration($finalData); + + if (isset($apiResponse->data)) { + $res = [$this->typeName . ' successfully']; + LogHandler::save($this->integrationId, wp_json_encode(['type' => $this->type, 'type_name' => $this->typeName]), 'success', wp_json_encode($res)); + } else { + LogHandler::save($this->integrationId, wp_json_encode(['type' => $this->type, 'type_name' => $this->type . ' creating']), 'error', wp_json_encode($apiResponse)); + } + + return $apiResponse; + } +} diff --git a/backend/Actions/Livestorm/Routes.php b/backend/Actions/Livestorm/Routes.php new file mode 100644 index 000000000..6e1e90a4e --- /dev/null +++ b/backend/Actions/Livestorm/Routes.php @@ -0,0 +1,12 @@ +id, 'Send Mail', 'failed', wp_sprintf(__('%s failed sends mail to %s', 'bit-integrations'), $flow->name, implode(', ', $mailTo))); + // translators: %s: Placeholder value + LogHandler::save($integrationData->id, 'Send Mail', 'failed', wp_sprintf(__('%1$s failed sends mail to %2$s', 'bit-integrations'), $flow->name, implode(', ', $mailTo))); } else { - LogHandler::save($integrationData->id, 'Send Mail', 'success', wp_sprintf(__('%s successfully sends mail to %s', 'bit-integrations'), $flow->name, implode(', ', $mailTo))); + // translators: %s: Placeholder value + LogHandler::save($integrationData->id, 'Send Mail', 'success', wp_sprintf(__('%1$s successfully sends mail to %2$s', 'bit-integrations'), $flow->name, implode(', ', $mailTo))); } remove_filter('wp_mail_content_type', [self::class, 'filterMailContentType']); diff --git a/includes/Actions/MailBluster/MailBlusterController.php b/backend/Actions/MailBluster/MailBlusterController.php similarity index 94% rename from includes/Actions/MailBluster/MailBlusterController.php rename to backend/Actions/MailBluster/MailBlusterController.php index df691038d..a17d13e91 100644 --- a/includes/Actions/MailBluster/MailBlusterController.php +++ b/backend/Actions/MailBluster/MailBlusterController.php @@ -4,9 +4,9 @@ * MailBluster Integration */ -namespace BitCode\FI\Actions\MailBluster; +namespace BitApps\Integrations\Actions\MailBluster; -use BitCode\FI\Core\Util\HttpHelper; +use BitApps\Integrations\Core\Util\HttpHelper; use WP_Error; /** @@ -67,6 +67,7 @@ public function execute($integrationData, $fieldValues) empty($fieldMap) || empty($auth_token) || empty($subscribed) ) { + // translators: %s: Placeholder value return new WP_Error('REQ_FIELD_EMPTY', wp_sprintf(__('module, fields are required for %s api', 'bit-integrations'), 'GetResponse')); } $recordApiHelper = new RecordApiHelper($integrationDetails, $integId); diff --git a/backend/Actions/MailBluster/RecordApiHelper.php b/backend/Actions/MailBluster/RecordApiHelper.php new file mode 100644 index 000000000..283b6e8a0 --- /dev/null +++ b/backend/Actions/MailBluster/RecordApiHelper.php @@ -0,0 +1,110 @@ +_integrationDetails = $integrationDetails; + $this->_integrationID = $integId; + $this->_defaultHeader = [ + 'Authorization' => $this->_integrationDetails->auth_token + ]; + } + + public function addLeadToBrand($selectedTags, $finalData, $subscribed) + { + $apiEndpoints = $this->baseUrl . 'leads'; + $tags = []; + + if (!empty($selectedTags)) { + $splitSelectedTags = explode(',', $selectedTags); + foreach ($splitSelectedTags as $tag) { + $tags[] = $tag; + } + } + + if (empty($finalData['email'])) { + return ['success' => false, 'message' => __('Required field Email is empty', 'bit-integrations'), 'code' => 400]; + } + + $requestParams = [ + 'subscribed' => $subscribed === 'true' ? true : false + ]; + + if (!empty($this->_integrationDetails->actions->update)) { + $requestParams['overrideExisting'] = true; + } + if (!empty($this->_integrationDetails->actions->doubleOptIn)) { + $requestParams['doubleOptIn'] = true; + } + + $staticFieldsKeys = ['email', 'firstName', 'lastName', 'timezone', 'ipAddress']; + $customFields = []; + + foreach ($finalData as $key => $value) { + if (\in_array($key, $staticFieldsKeys)) { + $requestParams[$key] = $value; + } else { + $customFields[$key] = $value; + } + } + + if (!empty($customFields)) { + $requestParams['fields'] = (object) $customFields; + } + + if (!empty($tags)) { + $requestParams['tags'] = $tags; + } + + return HttpHelper::post($apiEndpoints, $requestParams, $this->_defaultHeader); + } + + public function generateReqDataFromFieldMap($data, $fieldMap) + { + $dataFinal = []; + foreach ($fieldMap as $value) { + $triggerValue = $value->formField; + $actionValue = $value->mailBlusterFormField; + if ($triggerValue === 'custom') { + $dataFinal[$actionValue] = Common::replaceFieldWithValue($value->customValue, $data); + } elseif (!\is_null($data[$triggerValue])) { + $dataFinal[$actionValue] = $data[$triggerValue]; + } + } + + return $dataFinal; + } + + public function execute($selectedTag, $fieldValues, $fieldMap, $subscribed) + { + $finalData = $this->generateReqDataFromFieldMap($fieldValues, $fieldMap); + $apiResponse = $this->addLeadToBrand($selectedTag, $finalData, $subscribed); + + if ($apiResponse->lead->id) { + $res = ['message' => $apiResponse->message . ' successfully']; + LogHandler::save($this->_integrationID, wp_json_encode(['type' => 'lead', 'type_name' => $apiResponse->message]), 'success', wp_json_encode($res)); + } else { + LogHandler::save($this->_integrationID, wp_json_encode(['type' => 'lead', 'type_name' => 'Update lead']), 'error', wp_json_encode($apiResponse)); + } + + return $apiResponse; + } +} diff --git a/backend/Actions/MailBluster/Routes.php b/backend/Actions/MailBluster/Routes.php new file mode 100644 index 000000000..9a5b84170 --- /dev/null +++ b/backend/Actions/MailBluster/Routes.php @@ -0,0 +1,10 @@ +_integrationID, $integrationDetails); diff --git a/backend/Actions/MailChimp/RecordApiHelper.php b/backend/Actions/MailChimp/RecordApiHelper.php new file mode 100644 index 000000000..c0c91f3f8 --- /dev/null +++ b/backend/Actions/MailChimp/RecordApiHelper.php @@ -0,0 +1,175 @@ +_defaultHeader['Authorization'] = "Bearer {$tokenDetails->access_token}"; + $this->_defaultHeader['Content-Type'] = 'application/json'; + $this->_tokenDetails = $tokenDetails; + $this->_integrationID = $integId; + $this->_integrationDetails = $integrationDetails; + } + + public function insertRecord($listId, $data) + { + $insertRecordEndpoint = $this->_apiEndPoint() . "/lists/{$listId}/members"; + + return HttpHelper::post($insertRecordEndpoint, $data, $this->_defaultHeader); + } + + public function addRemoveTag($module, $listId, $data) + { + // translators: %s: Plugin name + $msg = wp_sprintf(__('%s plugin is not installed or activate', 'bit-integrations'), 'Bit Integrations Pro'); + $subscriber_hash = md5(strtolower(trim($data['email_address']))); + $endpoint = $this->_apiEndPoint() . "/lists/{$listId}/members/{$subscriber_hash}/tags"; + + $response = Hooks::apply(Config::withPrefix('mailchimp_add_remove_tag'), $module, $data, $endpoint, $this->_defaultHeader); + + /** + * @deprecated 2.7.8 Use `bit_integrations_mailchimp_add_remove_tag` filter instead. + * @since 2.7.8 + */ + $response = Hooks::apply('btcbi_mailchimp_add_remove_tag', $response, $data, $endpoint, $this->_defaultHeader); + + if (\is_string($response) && $response == $module) { + LogHandler::save($this->_integrationID, ['type' => 'record', 'type_name' => $module], 'error', $msg); + + return (object) ['status' => 400, 'message' => $msg]; + } + + return $response; + } + + public function updateRecord($listId, $contactId, $data) + { + $updateRecordEndpoint = $this->_apiEndPoint() . "/lists/{$listId}/members/{$contactId}"; + + return HttpHelper::request($updateRecordEndpoint, 'PUT', $data, $this->_defaultHeader); + } + + public function existContact($listId, $queryParam) + { + $existSearchEnpoint = $this->_apiEndPoint() . "/search-members?query={$queryParam}&list_id={$listId}"; + + return HttpHelper::get($existSearchEnpoint, null, $this->_defaultHeader); + } + + public function execute($listId, $module, $tags, $defaultConf, $fieldValues, $fieldMap, $actions, $addressFields) + { + $fieldData = static::generateFieldMap($fieldMap, $fieldValues, $actions, $addressFields, $tags); + + if (empty($module) || $module == 'add_a_member_to_an_audience') { + $fieldData = Hooks::apply(Config::withPrefix('mailchimp_map_language'), $fieldData, $this->_integrationDetails); + + /** + * @deprecated 2.7.8 Use `bit_integrations_mailchimp_map_language` filter instead. + * @since 2.7.8 + */ + $fieldData = Hooks::apply('btcbi_mailchimp_map_language', $fieldData, $this->_integrationDetails); + + $contactEmail = $fieldData['email_address']; + $foundContact = $this->existContact($listId, $contactEmail); + + if (!empty($actions->update) && !empty($foundContact->exact_matches->members)) { + $contactId = $foundContact->exact_matches->members[0]->id; + $fieldData['status'] = $foundContact->exact_matches->members[0]->status; + $recordApiResponse = $this->updateRecord($listId, $contactId, wp_json_encode($fieldData)); + $type = 'update'; + } else { + $recordApiResponse = $this->insertRecord($listId, wp_json_encode($fieldData)); + $type = 'insert'; + } + + if (isset($recordApiResponse->id, $this->_integrationDetails->selectedGDPR)) { + Hooks::run(Config::withPrefix('mailchimp_store_gdpr_permission'), $recordApiResponse, $this->_integrationDetails->selectedGDPR, $listId, $this->_apiEndPoint(), $this->_defaultHeader, $this->_integrationID); + + /** + * @deprecated 2.7.8 Use `bit_integrations_mailchimp_store_gdpr_permission` action instead. + * @since 2.7.8 + */ + Hooks::run('btcbi_mailchimp_store_gdpr_permission', $recordApiResponse, $this->_integrationDetails->selectedGDPR, $listId, $this->_apiEndPoint(), $this->_defaultHeader, $this->_integrationID); + } + } elseif ($module == 'add_tag_to_a_member' || $module == 'remove_tag_from_a_member') { + $type = $module; + $recordApiResponse = $this->addRemoveTag($module, $listId, $fieldData); + } + + if (isset($recordApiResponse->status) && ($recordApiResponse->status === 400 || $recordApiResponse->status === 404)) { + LogHandler::save($this->_integrationID, ['type' => 'record', 'type_name' => $type], 'error', wp_json_encode($recordApiResponse)); + } else { + LogHandler::save($this->_integrationID, ['type' => 'record', 'type_name' => $type], 'success', wp_json_encode($recordApiResponse)); + } + + return $recordApiResponse; + } + + private static function generateFieldMap($fieldMap, $fieldValues, $actions, $addressFields, $tags) + { + $fieldData = []; + $mergeFields = []; + foreach ($fieldMap as $fieldKey => $fieldPair) { + if (!empty($fieldPair->mailChimpField)) { + if ($fieldPair->mailChimpField === 'email_address') { + $fieldData['email_address'] = $fieldValues[$fieldPair->formField]; + } elseif ($fieldPair->mailChimpField === 'BIRTHDAY') { + $date = $fieldValues[$fieldPair->formField]; + $mergeFields[$fieldPair->mailChimpField] = gmdate('m/d', strtotime($date)); + } elseif ($fieldPair->formField === 'custom' && isset($fieldPair->customValue)) { + $mergeFields[$fieldPair->mailChimpField] = $fieldPair->customValue; + } else { + $mergeFields[$fieldPair->mailChimpField] = $fieldValues[$fieldPair->formField]; + } + } + } + + $doubleOptIn = !empty($actions->double_opt_in) && $actions->double_opt_in ? true : false; + + $fieldData['merge_fields'] = (object) $mergeFields; + // $fieldData['email_type'] = 'text'; + $fieldData['tags'] = !empty($tags) ? $tags : []; + $fieldData['status'] = $doubleOptIn ? 'pending' : 'subscribed'; + $fieldData['double_optin'] = $doubleOptIn; + if (!empty($actions->address)) { + $fvalue = []; + foreach ($addressFields as $key) { + foreach ($fieldValues as $k => $v) { + if ($key->formField == $k) { + $fvalue[$key->mailChimpAddressField] = $v; + } + } + } + $fieldData['merge_fields']->ADDRESS = (object) $fvalue; + } + + return $fieldData; + } + + private function _apiEndPoint() + { + return "https://{$this->_tokenDetails->dc}.api.mailchimp.com/3.0"; + } +} diff --git a/backend/Actions/MailChimp/Routes.php b/backend/Actions/MailChimp/Routes.php new file mode 100644 index 000000000..de4f997da --- /dev/null +++ b/backend/Actions/MailChimp/Routes.php @@ -0,0 +1,14 @@ +prefix . CustomFieldSchema::$table_name; + $fields_table = esc_sql($wpdb->prefix . CustomFieldSchema::$table_name); $primaryFields = get_option('mint_contact_primary_fields', Constants::$primary_contact_fields); - $customFields = $wpdb->get_results($wpdb->prepare('SELECT title, slug, type, group_id FROM %s ', $fields_table), ARRAY_A); + $cache_key = Config::withPrefix('mailmint_custom_fields'); + $cache_group = Config::VAR_PREFIX; + $customFields = wp_cache_get($cache_key, $cache_group); + + if (false === $customFields) { + // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.PreparedSQL.NotPrepared -- Querying MailMint custom field table directly; static table name from plugin schema. + $customFields = $wpdb->get_results('SELECT title, slug, type, group_id FROM `' . $fields_table . '`', ARRAY_A); + wp_cache_set($cache_key, $customFields, $cache_group, 10 * MINUTE_IN_SECONDS); + } if (!empty($customFields)) { $primaryFields['other'] = array_merge($primaryFields['other'], $customFields); @@ -48,6 +59,8 @@ public static function allCustomFields() } wp_send_json_success($allFields, 200); } + // translators: %s: Plugin name + // translators: %s: Placeholder value wp_send_json_error(wp_sprintf(__('%s must be activated!', 'bit-integrations'), 'Mail Mint')); } @@ -97,6 +110,9 @@ public function execute($integrationData, $fieldValues) empty($integId) || empty($mainAction) ) { + // translators: %s: Integration name + + // translators: %s: Placeholder value return new WP_Error('REQ_FIELD_EMPTY', wp_sprintf(__('module, fields are required for %s api', 'bit-integrations'), 'Mail Mint')); } $recordApiHelper = new RecordApiHelper($integrationDetails, $integId); diff --git a/backend/Actions/MailMint/RecordApiHelper.php b/backend/Actions/MailMint/RecordApiHelper.php new file mode 100644 index 000000000..3771c01ad --- /dev/null +++ b/backend/Actions/MailMint/RecordApiHelper.php @@ -0,0 +1,202 @@ +_integrationDetails = $integrationDetails; + self::$integrationID = $integId; + } + + public function generateReqDataFromFieldMap($data, $fieldMap) + { + $dataFinal = []; + + foreach ($fieldMap as $value) { + $triggerValue = $value->formField; + $actionValue = $value->mailMintFormField; + $isDataTriggerValueSet = isset($data[$triggerValue]); + $containsCustomMetaField = str_contains($actionValue, 'custom_meta_field_'); + + if ($containsCustomMetaField) { + $customFieldKey = str_replace('custom_meta_field_', '', $actionValue); + } + + if ($triggerValue === 'custom') { + if ($containsCustomMetaField) { + $dataFinal['meta_fields'][$customFieldKey] = Common::replaceFieldWithValue($value->customValue, $data); + } else { + $dataFinal[$actionValue] = Common::replaceFieldWithValue($value->customValue, $data); + } + } elseif ($isDataTriggerValueSet) { + if ($containsCustomMetaField) { + $dataFinal['meta_fields'][$customFieldKey] = $data[$triggerValue]; + } else { + $dataFinal[$actionValue] = $data[$triggerValue]; + } + } + } + + return $dataFinal; + } + + public function listFormat($selectedList) + { + $allLists = []; + if (class_exists('Mint\MRM\DataBase\Models\ContactGroupModel')) { + $listData = ContactGroupModel::get_all('lists'); + if (!empty($listData)) { + foreach ($listData['data'] as $list) { + if (\in_array($list['id'], $selectedList)) { + $allLists[] = [ + 'id' => $list['id'], + 'name' => $list['title'], + ]; + } + } + } + } + + return $allLists; + } + + public function tagFormat($selectedTags) + { + $allTags = []; + if (class_exists('Mint\MRM\DataBase\Models\ContactGroupModel')) { + $tagData = ContactGroupModel::get_all('tags'); + if (!empty($tagData)) { + foreach ($tagData['data'] as $list) { + if (\in_array($list['id'], $selectedTags)) { + $allTags[] = [ + 'id' => $list['id'], + 'name' => $list['title'], + ]; + } + } + } + } + + return $allTags; + } + + public function createContact($selectedList, $selectedTags, $selectedSubStatus, $finalData) + { + $selectedList = explode(',', $selectedList); + $selectedTags = explode(',', $selectedTags); + $listFormat = $this->listFormat($selectedList); + $tagFormat = $this->tagFormat($selectedTags); + + $finalData['status'] = $selectedSubStatus; + $finalData['_locale'] = 'user'; + $finalData['created_by'] = get_current_user_id(); + + $contact_id = null; + if (class_exists('Mint\MRM\DataStores\ContactData') && class_exists('Mint\MRM\DataBase\Models\ContactModel')) { + $contact = new ContactData($finalData['email'], $finalData); + $contact_id = ContactModel::insert($contact); + + if ('pending' === $selectedSubStatus) { + MessageController::get_instance()->send_double_opt_in($contact_id); + } + + if (isset($tagFormat)) { + ContactGroupModel::set_tags_to_contact($tagFormat, $contact_id); + } + + if (isset($listFormat)) { + ContactGroupModel::set_lists_to_contact($listFormat, $contact_id); + } + } + + return $contact_id; + } + + public function updateContact($selectedList, $selectedTags, $selectedSubStatus, $finalData, $contact_id) + { + $selectedList = explode(',', $selectedList); + $selectedTags = explode(',', $selectedTags); + + $listFormat = $this->listFormat($selectedList); + $tagFormat = $this->tagFormat($selectedTags); + + $finalData['_locale'] = 'user'; + $finalData['status'] = $selectedSubStatus; + $finalData['created_by'] = get_current_user_id(); + + if (class_exists('Mint\MRM\DataStores\ContactData') && class_exists('Mint\MRM\DataBase\Models\ContactModel')) { + ContactModel::update($finalData, $contact_id); + + if ('pending' === $selectedSubStatus) { + MessageController::get_instance()->send_double_opt_in($contact_id); + } + + if (isset($tagFormat)) { + ContactGroupModel::set_tags_to_contact($tagFormat, $contact_id); + } + + if (isset($listFormat)) { + ContactGroupModel::set_lists_to_contact($listFormat, $contact_id); + } + } + + return $contact_id; + } + + public function execute( + $mainAction, + $fieldValues, + $fieldMap, + $integrationDetails + ) { + $fieldData = []; + $apiResponse = null; + $update = $integrationDetails->update; + $finalData = $this->generateReqDataFromFieldMap($fieldValues, $fieldMap); + $contactExist = ContactModel::is_contact_exist($finalData['email']); + if ($mainAction === '1' && !$contactExist) { + $selectedList = $integrationDetails->selectedList; + $selectedTags = $integrationDetails->selectedTags; + $selectedSubStatus = $integrationDetails->selectedSubStatus; + $apiResponse = $this->createContact($selectedList, $selectedTags, $selectedSubStatus, $finalData); + + if ($apiResponse && \gettype($apiResponse) === 'integer') { + // translators: %s: Placeholder value + LogHandler::save(self::$integrationID, ['type' => 'create', 'type_name' => 'create contact'], 'success', wp_json_encode(wp_sprintf(__('Contact created successfully and id is %s', 'bit-integrations'), $apiResponse))); + } else { + LogHandler::save(self::$integrationID, ['type' => 'create', 'type_name' => 'create contact'], 'error', __('Failed to create contact', 'bit-integrations')); + } + + return $apiResponse; + } elseif ($mainAction === '1' && $contactExist && $update) { + $selectedList = $integrationDetails->selectedList; + $selectedTags = $integrationDetails->selectedTags; + $selectedSubStatus = $integrationDetails->selectedSubStatus; + $apiResponse = $this->updateContact($selectedList, $selectedTags, $selectedSubStatus, $finalData, $contactExist); + + if ($apiResponse && (is_numeric($apiResponse) || (\is_bool($apiResponse) && $apiResponse))) { + LogHandler::save(self::$integrationID, ['type' => 'update', 'type_name' => 'update contact'], 'success', __('Contact updated successfully', 'bit-integrations')); + } else { + LogHandler::save(self::$integrationID, ['type' => 'update', 'type_name' => 'update contact'], 'error', __('Failed to create contact', 'bit-integrations')); + } + + return $apiResponse; + } + LogHandler::save(self::$integrationID, ['type' => 'create', 'type_name' => 'create contact'], 'error', __('Email already exist', 'bit-integrations')); + + return $apiResponse; + } +} diff --git a/backend/Actions/MailMint/Routes.php b/backend/Actions/MailMint/Routes.php new file mode 100644 index 000000000..ca2c9bdba --- /dev/null +++ b/backend/Actions/MailMint/Routes.php @@ -0,0 +1,13 @@ +actions; if (empty($fieldMap)) { + // translators: %s: Placeholder value return new WP_Error('REQ_FIELD_EMPTY', wp_sprintf(__('module, fields are required for %s api', 'bit-integrations'), 'Google sheet')); } diff --git a/backend/Actions/MailPoet/RecordApiHelper.php b/backend/Actions/MailPoet/RecordApiHelper.php new file mode 100644 index 000000000..530370f2a --- /dev/null +++ b/backend/Actions/MailPoet/RecordApiHelper.php @@ -0,0 +1,157 @@ +_integrationID = $integId; + static::$mailPoet_api = \MailPoet\API\API::MP('v1'); + } + + public function insertRecord($subscriber, $lists, $actions) + { + try { + // try to find if user is already a subscriber + $existingSubscriber = static::$mailPoet_api->getSubscriber($subscriber['email']); + + if (!$existingSubscriber) { + return static::addSubscriber($subscriber, $lists); + } + + if (!empty($actions->update)) { + $response = Hooks::apply(Config::withPrefix('mailpoet_update_subscriber'), $existingSubscriber['id'], $subscriber); + + /** + * @deprecated 2.7.8 Use `bit_integrations_mailpoet_update_subscriber` filter instead. + * @since 2.7.8 + */ + $response = Hooks::apply('btcbi_mailpoet_update_subscriber', $response, $subscriber); + + if ($response === $existingSubscriber['id']) { + // translators: %s: Plugin name + $errorMessages = wp_sprintf(__('%s is not active or not installed', 'bit-integrations'), 'Bit Integrations Pro'); + } elseif (!$response['success']) { + $errorMessages = $response('message'); + } + + if (isset($errorMessages)) { + LogHandler::save($this->_integrationID, ['type' => 'record', 'type_name' => 'update'], 'error', $errorMessages); + } + } + + return static::addSubscribeToLists($existingSubscriber['id'], $lists); + } catch (\MailPoet\API\MP\v1\APIException $e) { + if ($e->getCode() == 4) { + // Handle the case where the subscriber doesn't exist + return static::addSubscriber($subscriber, $lists); + } + + return [ + 'success' => false, + 'code' => $e->getCode(), + 'message' => $e->getMessage() + ]; + } catch (Exception $e) { + // Handle other unexpected exceptions + return [ + 'success' => false, + 'code' => $e->getCode(), + 'message' => $e->getMessage(), + ]; + } + } + + public function execute($fieldValues, $fieldMap, $lists, $actions) + { + if (!class_exists(\MailPoet\API\API::class)) { + return; + } + + $fieldData = static::setFieldMap($fieldMap, $fieldValues); + $recordApiResponse = $this->insertRecord($fieldData, $lists, $actions); + + if ($recordApiResponse['success']) { + LogHandler::save($this->_integrationID, ['type' => 'record', 'type_name' => 'insert'], 'success', $recordApiResponse); + } else { + LogHandler::save($this->_integrationID, ['type' => 'record', 'type_name' => 'insert'], 'error', $recordApiResponse); + } + + return $recordApiResponse; + } + + private static function setFieldMap($fieldMap, $fieldValues) + { + $fieldData = []; + + foreach ($fieldMap as $fieldPair) { + if (empty($fieldPair->mailPoetField)) { + continue; + } + + $fieldData[$fieldPair->mailPoetField] = ($fieldPair->formField == 'custom' && !empty($fieldPair->customValue)) + ? Common::replaceFieldWithValue($fieldPair->customValue, $fieldValues) + : $fieldValues[$fieldPair->formField]; + } + + return $fieldData; + } + + private static function addSubscriber($subscriber, $lists) + { + try { + $subscriber = static::$mailPoet_api->addSubscriber($subscriber, $lists); + + return [ + 'success' => true, + 'id' => $subscriber['id'], + ]; + } catch (\MailPoet\API\MP\v1\APIException $e) { + return [ + 'success' => false, + 'code' => $e->getCode(), + 'message' => $e->getMessage(), + ]; + } + } + + private static function addSubscribeToLists($subscriber_id, $lists) + { + try { + $subscriber = static::$mailPoet_api->subscribeToLists($subscriber_id, $lists); + + return [ + 'success' => true, + 'id' => $subscriber['id'], + ]; + } catch (\MailPoet\API\MP\v1\APIException $e) { + return [ + 'success' => false, + 'code' => $e->getCode(), + 'message' => $e->getMessage(), + ]; + } + } +} diff --git a/backend/Actions/MailPoet/Routes.php b/backend/Actions/MailPoet/Routes.php new file mode 100644 index 000000000..63682515d --- /dev/null +++ b/backend/Actions/MailPoet/Routes.php @@ -0,0 +1,12 @@ +_integrationDetails = $integrationDetails; + $this->_integrationID = $integId; + $this->_domainName = $this->_integrationDetails->domain; + $this->_defaultHeader = [ + 'X-AUTH-TOKEN' => $this->_integrationDetails->auth_token, + 'Content-Type' => 'application/json' + ]; + } + + public function addSubscriber($selectedGroups, $finalData, $status) + { + $baseUrl = "https://{$this->_domainName}.ipzmarketing.com/api/v1/"; + $apiEndpoints = $baseUrl . 'subscribers'; + $groups = []; + + if (!empty($selectedGroups)) { + $splitSelectedGroups = explode(',', $selectedGroups); + foreach ($splitSelectedGroups as $group) { + $groups[] = $group; + } + } + + if (empty($finalData['email'])) { + return ['success' => false, 'message' => __('Required field Email is empty', 'bit-integrations'), 'code' => 400]; + } + + $isSubscriberExist = $this->isExist($baseUrl, $finalData['email']); + $requestParams = [ + 'status' => $status + ]; + + $staticFieldsKeys = ['email', 'name', 'sms_phone', 'address', 'city', 'state', 'country', 'birthday', 'website', 'locale', 'timezone']; + $customFields = []; + + foreach ($finalData as $key => $value) { + if (\in_array($key, $staticFieldsKeys) && $key == 'sms_phone') { + $requestParams[$key] = Helper::formatPhoneNumber($value); + } elseif (\in_array($key, $staticFieldsKeys)) { + $requestParams[$key] = $value; + } else { + $customFields[$key] = $value; + } + } + + if (!empty($customFields)) { + $requestParams['custom_fields'] = (object) $customFields; + } + + if (!empty($groups)) { + $requestParams['group_ids'] = $groups; + } + + $apiRequestBody = wp_json_encode($requestParams); + + if ($isSubscriberExist && !empty($this->_integrationDetails->actions->update)) { + $apiEndpoints = $baseUrl . 'subscribers/' . $isSubscriberExist; + $this->_requestStoringTypes = 'updated'; + + return HttpHelper::request($apiEndpoints, 'PATCH', $apiRequestBody, $this->_defaultHeader); + } + + $this->_requestStoringTypes = 'created'; + + return HttpHelper::post($apiEndpoints, $apiRequestBody, $this->_defaultHeader); + } + + public function generateReqDataFromFieldMap($data, $fieldMap) + { + $dataFinal = []; + foreach ($fieldMap as $value) { + $triggerValue = $value->formField; + $actionValue = $value->mailRelayFormField; + if ($triggerValue === 'custom') { + $dataFinal[$actionValue] = Common::replaceFieldWithValue($value->customValue, $data); + } elseif (!\is_null($data[$triggerValue])) { + $dataFinal[$actionValue] = $data[$triggerValue]; + } + } + + return $dataFinal; + } + + public function execute($selectedGroups, $fieldValues, $fieldMap, $status) + { + $finalData = $this->generateReqDataFromFieldMap($fieldValues, $fieldMap); + $apiResponse = $this->addSubscriber($selectedGroups, $finalData, $status); + + if (isset($apiResponse->id)) { + $res = ['message' => 'Subscriber ' . $this->_requestStoringTypes . ' successfully']; + LogHandler::save($this->_integrationID, wp_json_encode(['type' => 'subscriber', 'type_name' => 'Subscriber ' . $this->_requestStoringTypes]), 'success', wp_json_encode($res)); + } else { + LogHandler::save($this->_integrationID, wp_json_encode(['type' => 'subscriber', 'type_name' => 'Adding Subscriber']), 'error', wp_json_encode($apiResponse)); + } + + return $apiResponse; + } + + public function isExist($baseUrl, $email) + { + $queryEndpoints = $baseUrl . 'subscribers?q%5Bemail_eq%5D='; + $encodedEmail = urlencode($email); + $apiEndpoints = $queryEndpoints . $encodedEmail; + $response = HttpHelper::get($apiEndpoints, null, $this->_defaultHeader); + + if (isset($response[0]->id)) { + return $response[0]->id; + } + + return false; + } +} diff --git a/backend/Actions/MailRelay/Routes.php b/backend/Actions/MailRelay/Routes.php new file mode 100644 index 000000000..f3a04dd97 --- /dev/null +++ b/backend/Actions/MailRelay/Routes.php @@ -0,0 +1,11 @@ +_integrationDetails = $integrationDetails; + $this->_integrationID = $integId; + $this->_isMailerLiteV2 = (bool) ('v2' === $version); + + if ('v2' === $version) { + $this->_baseUrl = 'https://connect.mailerlite.com/api/'; + $this->_defaultHeader = [ + 'Authorization' => "Bearer {$auth_token}" + ]; + } else { + $this->_baseUrl = 'https://api.mailerlite.com/api/v2/'; + $this->_defaultHeader = [ + 'X-Mailerlite-Apikey' => $auth_token + ]; + } + $this->_actions = $actions; + } + + public function existSubscriber($auth_token, $email) + { + if (empty($auth_token)) { + wp_send_json_error( + __( + 'Requested parameter is empty', + 'bit-integrations' + ), + 400 + ); + } + + $apiEndpoints = $this->_baseUrl . "subscribers/{$email}"; + + $response = HttpHelper::get($apiEndpoints, null, $this->_defaultHeader); + + return $response->data->id ?? false; + } + + public function enableDoubleOptIn($auth_token) + { + if (empty($auth_token)) { + wp_send_json_error( + __( + 'Requested parameter is empty', + 'bit-integrations' + ), + 400 + ); + } + + $apiEndpoints = $this->_baseUrl . 'settings/double_optin'; + $requestParams = [ + 'enable' => true + ]; + + HttpHelper::post($apiEndpoints, $requestParams, $this->_defaultHeader); + } + + public function addSubscriber($auth_token, $groupIds, $type, $finalData) + { + if (empty($finalData['email'])) { + return [ + 'success' => false, + 'message' => __('Required field Email is empty', 'bit-integrations'), + 'code' => 400 + ]; + } + + $email = $finalData['email']; + $splitGroupIds = !empty($groupIds) ? explode(',', $groupIds) : []; + $apiEndpoint = $this->_baseUrl . 'subscribers'; + + $requestParams = self::prepareRequestParams($finalData, $type, $this->_isMailerLiteV2); + + $isExist = $this->existSubscriber($auth_token, $email); + $response = null; + + if ($isExist && empty($this->_actions->update)) { + return [ + 'success' => false, + 'message' => __('Subscriber already exist', 'bit-integrations'), + 'code' => 400 + ]; + } + + self::handleDoubleOptIn($this, $auth_token, $requestParams, $this->_isMailerLiteV2); + + if (!empty($splitGroupIds)) { + return self::sendToGroups($this, $splitGroupIds, $requestParams, $this->_isMailerLiteV2); + } + + if ($isExist) { + $response = HttpHelper::post($apiEndpoint, $requestParams, $this->_defaultHeader); + $response->update = true; + + return $response; + } + + return HttpHelper::post($apiEndpoint, $requestParams, $this->_defaultHeader); + } + + public function deleteSubscriber($auth_token, $finalData, $forget = false) + { + if (!$this->_isMailerLiteV2) { + return [ + 'success' => false, + 'message' => __('This action is not supported for Classic accounts.', 'bit-integrations'), + 'code' => 400 + ]; + } + + if (empty($finalData['email'])) { + return [ + 'success' => false, + 'message' => __('Required field Email is empty', 'bit-integrations'), + 'code' => 400 + ]; + } + + $subscriberId = $this->existSubscriber($auth_token, $finalData['email']); + + if (empty($subscriberId)) { + return [ + 'success' => false, + 'message' => __('Subscriber not exist', 'bit-integrations'), + 'code' => 400 + ]; + } + + $response = Hooks::apply(Config::withPrefix('mailerlite_delete_subscriber'), false, $subscriberId, $finalData, $this->_baseUrl, $this->_defaultHeader, $forget); + + /** + * @deprecated 2.7.8 Use `bit_integrations_mailerlite_delete_subscriber` filter instead. + * @since 2.7.8 + */ + $response = Hooks::apply('btcbi_mailerlite_delete_subscriber', $response, $subscriberId, $finalData, $this->_baseUrl, $this->_defaultHeader, $forget); + + return $response ? $response : (object) ['success' => false, 'message' => __('Bit Integrations Pro is required.', 'bit-integrations'), 'code' => 400]; + } + + public function generateReqDataFromFieldMap($data, $fieldMap) + { + $dataFinal = []; + + foreach ($fieldMap as $key => $value) { + $triggerValue = $value->formField; + $actionValue = $value->mailerLiteFormField; + if ($triggerValue === 'custom') { + $dataFinal[$actionValue] = Common::replaceFieldWithValue($value->customValue, $data); + } elseif (!\is_null($data[$triggerValue])) { + $dataFinal[$actionValue] = $data[$triggerValue]; + } + } + + return $dataFinal; + } + + public function execute( + $groupId, + $type, + $fieldValues, + $fieldMap, + $auth_token, + $action + ) { + $finalData = $this->generateReqDataFromFieldMap($fieldValues, $fieldMap); + + switch ($action) { + case 'delete_subscriber': + $apiResponse = $this->deleteSubscriber($auth_token, $finalData); + $typeName = 'delete-subscriber'; + $res = ['success' => true, 'message' => __('Subscriber deleted successfully', 'bit-integrations'), 'code' => 200]; + + break; + + case 'forget_subscriber': + $apiResponse = $this->deleteSubscriber($auth_token, $finalData, true); + $typeName = 'forget-subscriber'; + $res = $apiResponse->message ?? wp_json_encode($apiResponse); + + break; + + default: + $apiResponse = $this->addSubscriber($auth_token, $groupId, $type, $finalData); + $typeName = 'add-subscriber'; + $res = ['success' => true, 'message' => isset($apiResponse->update) ? __('Subscriber updated successfully', 'bit-integrations') : __('Subscriber created successfully', 'bit-integrations'), 'code' => 200]; + + break; + } + + if (isset($apiResponse->data->id) || isset($apiResponse->id) || str_starts_with((string) HttpHelper::$responseCode, '20')) { + LogHandler::save($this->_integrationID, wp_json_encode(['type' => 'subscriber', 'type_name' => $typeName]), 'success', wp_json_encode($res)); + } else { + LogHandler::save($this->_integrationID, wp_json_encode(['type' => 'subscriber', 'type_name' => $typeName]), 'error', wp_json_encode($apiResponse)); + } + + return $apiResponse; + } + + private static function prepareRequestParams($finalData, $type, $isMailerLiteV2) + { + $email = $finalData['email']; + $params = [ + 'email' => $email, + $isMailerLiteV2 ? 'status' : 'type' => $type ? $type : 'active', + ]; + + foreach ($finalData as $key => $value) { + if ($key !== 'email') { + $params['fields'][$key] = $value; + } + } + + $params['fields'] = !empty($params['fields']) ? (object) $params['fields'] : []; + + return $params; + } + + private static function handleDoubleOptIn($context, $auth_token, &$requestParams, $isMailerLiteV2) + { + if (empty($context->_actions->double_opt_in)) { + return; + } + + if ($isMailerLiteV2) { + $requestParams['opted_in_at'] = gmdate('Y-m-d H:i:s'); + + // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Sanitized on next line + if (isset($_SERVER['REMOTE_ADDR'])) { + // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Sanitized below + $remoteAddr = wp_unslash($_SERVER['REMOTE_ADDR']); + $remoteAddr = sanitize_text_field($remoteAddr); + + $requestParams['optin_ip'] = $remoteAddr; + } + } else { + $context->enableDoubleOptIn($auth_token); + } + } + + private static function sendToGroups($context, $groupIds, $requestParams, $isMailerLiteV2) + { + $response = null; + + if ($isMailerLiteV2) { + $requestParams['groups'] = $groupIds; + $endpoint = $context->_baseUrl . 'subscribers'; + $response = HttpHelper::post($endpoint, $requestParams, $context->_defaultHeader); + } else { + foreach ($groupIds as $groupId) { + $endpoint = $context->_baseUrl . 'groups/' . $groupId . '/subscribers'; + $response = HttpHelper::post($endpoint, $requestParams, $context->_defaultHeader); + } + } + + return $response; + } +} diff --git a/backend/Actions/MailerLite/Routes.php b/backend/Actions/MailerLite/Routes.php new file mode 100644 index 000000000..69e2a195a --- /dev/null +++ b/backend/Actions/MailerLite/Routes.php @@ -0,0 +1,12 @@ +tags ?? []); if (empty($fieldMap)) { + // translators: %s: Integration name return new WP_Error('REQ_FIELD_EMPTY', wp_sprintf(__('Field map is required for %s api', 'bit-integrations'), 'MailerPress')); } @@ -140,4 +141,3 @@ public function execute($integrationData, $fieldValues) return $mailerPressApiResponse; } } - diff --git a/backend/Actions/MailerPress/RecordApiHelper.php b/backend/Actions/MailerPress/RecordApiHelper.php new file mode 100644 index 000000000..c5f51d750 --- /dev/null +++ b/backend/Actions/MailerPress/RecordApiHelper.php @@ -0,0 +1,241 @@ +_integrationID = $integId; + } + + /** + * Execute the integration + * + * @param array $fieldValues Field values from form + * @param array $fieldMap Field mapping + * @param array $lists Lists to subscribe + * @param array $tags Tags to add + * @param mixed $mainAction + * + * @return array + */ + public function execute($fieldValues, $fieldMap, $lists, $tags, $mainAction) + { + if (!class_exists('\MailerPress\Core\Kernel')) { + return [ + 'success' => false, + 'message' => __('MailerPress is not installed or activated', 'bit-integrations') + ]; + } + + $fieldData = static::setFieldMap($fieldMap, $fieldValues); + + $defaultResponse = [ + 'success' => false, + // translators: %s: Plugin name + 'message' => wp_sprintf(__('%s plugin is not installed or activate', 'bit-integrations'), 'Bit Integrations') + ]; + + // Route to appropriate action method + switch ($mainAction) { + case 'create_or_update_contact': + $response = $this->insertRecord($fieldData, $lists, $tags); + $actionType = 'create'; + + break; + + case 'delete_contact': + $response = Hooks::apply(Config::withPrefix('mailerpress_delete_contact'), $defaultResponse, $fieldData); + + /** + * @deprecated 2.7.8 Use `bit_integrations_mailerpress_delete_contact` filter instead. + * @since 2.7.8 + */ + $response = Hooks::apply('btcbi_mailerpress_delete_contact', $response, $fieldData); + $actionType = 'delete'; + + break; + + case 'add_tags': + $response = Hooks::apply(Config::withPrefix('mailerpress_add_tags'), $defaultResponse, $fieldData, $tags); + + /** + * @deprecated 2.7.8 Use `bit_integrations_mailerpress_add_tags` filter instead. + * @since 2.7.8 + */ + $response = Hooks::apply('btcbi_mailerpress_add_tags', $response, $fieldData, $tags); + $actionType = 'add_tags'; + + break; + + case 'remove_tags': + $response = Hooks::apply(Config::withPrefix('mailerpress_remove_tags'), $defaultResponse, $fieldData, $tags); + + /** + * @deprecated 2.7.8 Use `bit_integrations_mailerpress_remove_tags` filter instead. + * @since 2.7.8 + */ + $response = Hooks::apply('btcbi_mailerpress_remove_tags', $response, $fieldData, $tags); + $actionType = 'remove_tags'; + + break; + + case 'add_to_lists': + $response = Hooks::apply(Config::withPrefix('mailerpress_add_to_lists'), $defaultResponse, $fieldData, $lists); + + /** + * @deprecated 2.7.8 Use `bit_integrations_mailerpress_add_to_lists` filter instead. + * @since 2.7.8 + */ + $response = Hooks::apply('btcbi_mailerpress_add_to_lists', $response, $fieldData, $lists); + $actionType = 'add_to_lists'; + + break; + + case 'remove_from_lists': + $response = Hooks::apply(Config::withPrefix('mailerpress_remove_from_lists'), $defaultResponse, $fieldData, $lists); + + /** + * @deprecated 2.7.8 Use `bit_integrations_mailerpress_remove_from_lists` filter instead. + * @since 2.7.8 + */ + $response = Hooks::apply('btcbi_mailerpress_remove_from_lists', $response, $fieldData, $lists); + $actionType = 'remove_from_lists'; + + break; + + default: + $response = [ + 'success' => false, + 'message' => __('Invalid action', 'bit-integrations') + ]; + $actionType = 'create'; + + break; + } + + if ($response['success']) { + LogHandler::save($this->_integrationID, ['type' => 'Contact', 'type_name' => $actionType], 'success', $response); + } else { + LogHandler::save($this->_integrationID, ['type' => 'Contact', 'type_name' => $actionType], 'error', $response); + } + + return $response; + } + + /** + * Insert or update contact record + * + * @param array $contactData Contact data + * @param array $lists Lists to subscribe + * @param array $tags Tags to add + * + * @return array + */ + private function insertRecord($contactData, $lists, $tags) + { + $email = key_exists('email', $contactData) ? sanitize_email($contactData['email']) : null; + + if (\is_null($email)) { + return [ + 'success' => false, + 'message' => __('Email is required', 'bit-integrations') + ]; + } + + $tagIds = []; + if (\is_array($tags)) { + $tagIds = array_map( + function ($id) { + return ['id' => $id]; + }, + $tags + ); + } + + $listIds = []; + if (\is_array($lists)) { + $listIds = array_map( + function ($id) { + return ['id' => $id]; + }, + $lists + ); + } + + if (!\function_exists('add_mailerpress_contact')) { + return [ + 'success' => false, + 'message' => __('MailerPress is not installed or activated', 'bit-integrations') + ]; + } + + $mailerPressContact = [ + 'contactEmail' => $email, + 'contactFirstName' => isset($contactData['first_name']) ? sanitize_text_field($contactData['first_name']) : '', + 'contactLastName' => isset($contactData['last_name']) ? sanitize_text_field($contactData['last_name']) : '', + 'contactStatus' => isset($contactData['status']) ? sanitize_text_field($contactData['status']) : 'subscribed', + 'subscription_status' => isset($contactData['status']) ? sanitize_text_field($contactData['status']) : 'subscribed', + 'opt_in_source' => 'bit-integrations', + 'tags' => $tagIds, + 'lists' => $listIds, + ]; + + $result = add_mailerpress_contact($mailerPressContact); + + if (isset($result['success']) && $result['success']) { + $isUpdate = $result['update'] ?? false; + + return [ + 'success' => true, + 'result' => $result, + 'message' => $isUpdate ? __('Contact updated successfully', 'bit-integrations') : __('Contact created successfully', 'bit-integrations') + ]; + } + + return [ + 'success' => false, + 'message' => $result['message'] ?? __('Failed to create or update contact', 'bit-integrations') + ]; + } + + /** + * Map form fields to MailerPress fields + * + * @param array $fieldMap Field mapping + * @param array $fieldValues Field values + * + * @return array + */ + private static function setFieldMap($fieldMap, $fieldValues) + { + $fieldData = []; + + foreach ($fieldMap as $fieldPair) { + if (!isset($fieldPair->mailerPressField) || \is_null($fieldPair->mailerPressField) || $fieldPair->mailerPressField === '') { + continue; + } + + $fieldData[$fieldPair->mailerPressField] = ($fieldPair->formField == 'custom' && isset($fieldPair->customValue)) + ? Common::replaceFieldWithValue($fieldPair->customValue, $fieldValues) + : $fieldValues[$fieldPair->formField]; + } + + return $fieldData; + } +} diff --git a/backend/Actions/MailerPress/Routes.php b/backend/Actions/MailerPress/Routes.php new file mode 100644 index 000000000..19708b49f --- /dev/null +++ b/backend/Actions/MailerPress/Routes.php @@ -0,0 +1,12 @@ +_integrationDetails = $integrationDetails; + $this->_integrationID = $integId; + } + + public function addContact($authKey, $data) + { + if (empty($data->email)) { + return ['success' => false, 'message' => __('Required field opportunity name is empty', 'bit-integrations'), 'code' => 400]; + } + $staticFieldsKeys = ['city', 'country', 'details', 'department', 'dob', 'email', 'industry', 'job_title', 'last_name', 'lead_source', 'middle_name', 'name', 'organization', 'phone', 'salary', 'state', 'zip', 'contact_type', 'list_id']; + + foreach ($data as $key => $value) { + if (\in_array($key, $staticFieldsKeys)) { + $finalData[$key] = $value; + } else { + $finalData['custom_fields'][$key] = $value; + } + } + + $apiEndpoints = "{$this->baseUrl}contacts"; + $headers = [ + 'Content-Type' => 'application/json', + 'Authorization' => $authKey + ]; + + return HttpHelper::post($apiEndpoints, wp_json_encode($finalData), $headers); + } + + public function generateReqDataFromFieldMap($data, $field_map) + { + $dataFinal = []; + + foreach ($field_map as $key => $value) { + $triggerValue = $value->formField; + $actionValue = $value->mailercloudFormField; + if ($triggerValue === 'custom') { + $dataFinal[$actionValue] = Common::replaceFieldWithValue($value->customValue, $data); + } elseif (!\is_null($data[$triggerValue])) { + $dataFinal[$actionValue] = $data[$triggerValue]; + } + } + + return $dataFinal; + } + + public function response($status, $code, $type, $typeName, $apiResponse) + { + $res = ['success' => $code === 200 ? true : false, 'message' => $apiResponse, 'code' => $code]; + LogHandler::save($this->_integrationID, wp_json_encode(['type' => $type, 'type_name' => $typeName]), $status, wp_json_encode($res)); + + return $res; + } + + public function execute( + $listId, + $contactType, + $fieldValues, + $field_map, + $authKey + ) { + $finalData = $this->generateReqDataFromFieldMap($fieldValues, $field_map); + $data = (object) $finalData; + $data->list_id = $listId; + $data->contact_type = $contactType; + $apiResponse = $this->addContact($authKey, $data); + if ($apiResponse->errors) { + $this->response('error', 400, 'contact', 'add-contact', $apiResponse); + } else { + $this->response('success', 200, 'contact', 'add-contact', $apiResponse); + } + + return $apiResponse; + } +} diff --git a/backend/Actions/Mailercloud/Routes.php b/backend/Actions/Mailercloud/Routes.php new file mode 100644 index 000000000..1372cf5e0 --- /dev/null +++ b/backend/Actions/Mailercloud/Routes.php @@ -0,0 +1,12 @@ +api_key; if (empty($fieldMap) || empty($accountId) || empty($apiKey) || empty($selectedList)) { + // translators: %s: Placeholder value return new WP_Error('REQ_FIELD_EMPTY', wp_sprintf(__('module, fields are required for %s api', 'bit-integrations'), 'Mailify')); } diff --git a/backend/Actions/Mailify/RecordApiHelper.php b/backend/Actions/Mailify/RecordApiHelper.php new file mode 100644 index 000000000..46d629e45 --- /dev/null +++ b/backend/Actions/Mailify/RecordApiHelper.php @@ -0,0 +1,104 @@ +_integrationDetails = $integrationDetails; + $this->_integrationID = $integId; + $this->_defaultHeader = [ + 'Authorization' => 'Basic ' . base64_encode("{$accountId}:{$apiKey}"), + 'Content-Type' => 'application/json' + ]; + } + + // for updating contact data through email id. + public function updateContact($id, $data, $existContact, $selectedList) + { + $contactData = $data; + $apiEndpoints = "https://mailifyapis.com/v1/lists/{$selectedList}/contacts/{$id}"; + + return HttpHelper::request($apiEndpoints, 'PUT', wp_json_encode($contactData), $this->_defaultHeader); + } + + public function addContact($selectedList, $finalData) + { + $apiEndpoints = "https://mailifyapis.com/v1/lists/{$selectedList}/contacts"; + + return HttpHelper::post($apiEndpoints, wp_json_encode($finalData), $this->_defaultHeader); + } + + public function generateReqDataFromFieldMap($data, $fieldMap) + { + $dataFinal = []; + foreach ($fieldMap as $value) { + $triggerValue = $value->formField; + $actionValue = $value->mailifyField; + + if ($triggerValue === 'custom') { + $dataFinal[$actionValue] = $value->customValue; + } elseif (!\is_null($data[$triggerValue])) { + if ($actionValue === 'EMAIL_ID') { + $dataFinal['email'] = $data[$triggerValue]; + } elseif ($actionValue === 'PHONE_ID') { + $dataFinal['phone'] = $data[$triggerValue]; + } else { + $dataFinal[$actionValue] = $data[$triggerValue]; + } + } + } + + return $dataFinal; + } + + public function execute($selectedList, $fieldValues, $fieldMap, $actions) + { + $finalData = (object) $this->generateReqDataFromFieldMap($fieldValues, $fieldMap); + $existContact = $this->existContact($selectedList, $finalData->email); + + if ($existContact === 'null') { + $apiResponse = $this->addContact($selectedList, $finalData); + + if ($apiResponse) { + $res = ['message' => 'Contact added successfully']; + LogHandler::save($this->_integrationID, wp_json_encode(['type' => 'contact', 'type_name' => 'Contact added']), 'success', wp_json_encode($res)); + } else { + LogHandler::save($this->_integrationID, wp_json_encode(['type' => 'contact', 'type_name' => 'Adding Contact']), 'error', wp_json_encode($apiResponse)); + } + } else { + if ($actions->update) { + $apiResponse = $this->updateContact($existContact[0]->id, $finalData, $existContact[0], $selectedList); + if ($apiResponse) { + LogHandler::save($this->_integrationID, wp_json_encode(['type' => 'contact', 'type_name' => 'updating Contact']), 'error', wp_json_encode($apiResponse)); + } else { + $res = ['message' => 'Contact updated successfully']; + LogHandler::save($this->_integrationID, wp_json_encode(['type' => 'contact', 'type_name' => 'Contact updated']), 'success', wp_json_encode($res)); + } + } else { + LogHandler::save($this->_integrationID, ['type' => 'contact', 'type_name' => 'Adding Contact'], 'error', __('Email address already exists in the system', 'bit-integrations')); + wp_send_json_error(__('Email address already exists in the system', 'bit-integrations'), 400); + } + } + + return $apiResponse; + } + + // Check if a contact exists through email. + private function existContact($selectedList, $email) + { + $apiEndpoints = "https://mailifyapis.com/v1/lists/{$selectedList}/contacts?email={$email}"; + + return HttpHelper::get($apiEndpoints, null, $this->_defaultHeader); + } +} diff --git a/backend/Actions/Mailify/Routes.php b/backend/Actions/Mailify/Routes.php new file mode 100644 index 000000000..d8b315df6 --- /dev/null +++ b/backend/Actions/Mailify/Routes.php @@ -0,0 +1,12 @@ +field_map; if (empty($fieldMap) || empty($secretKey) || empty($apiKey) || empty($selectedLists)) { + // translators: %s: Placeholder value return new WP_Error('REQ_FIELD_EMPTY', wp_sprintf(__('module, fields are required for %s api', 'bit-integrations'), 'Mailjet')); } diff --git a/backend/Actions/Mailjet/RecordApiHelper.php b/backend/Actions/Mailjet/RecordApiHelper.php new file mode 100644 index 000000000..82f976cbd --- /dev/null +++ b/backend/Actions/Mailjet/RecordApiHelper.php @@ -0,0 +1,122 @@ +_integrationDetails = $integrationDetails; + $this->_integrationID = $integId; + $this->_defaultHeader = [ + 'Authorization' => 'Basic ' . base64_encode("{$apiKey}:{$secretKey}"), + 'Content-Type' => 'application/json' + ]; + } + + public function addSubscriber($selectedLists, $finalData) + { + $apiEndpoints = 'https://api.mailjet.com/v3/REST/contact/managemanycontacts'; + + if (empty($finalData['Email'])) { + return ['success' => false, 'message' => __('Required field Email is empty', 'bit-integrations'), 'code' => 400]; + } + + $this->_responseType = $this->isExist($finalData['Email']); + + if (!empty($selectedLists)) { + $aplitSelectedLists = explode(',', $selectedLists); + foreach ($aplitSelectedLists as $list) { + $contactsLists[] = (object) [ + 'Action' => 'addforce', + 'ListID' => $list + ]; + } + } + + $contacts['IsExcludedFromCampaigns'] = !empty($this->_integrationDetails->actions->IsExcludedFromCampaigns) ? true : false; + + foreach ($finalData as $key => $value) { + if ($key == 'Email') { + $contacts[$key] = $value; + } else { + $customFields[$key] = $value; + } + } + + if (!empty($customFields)) { + $contacts['Properties'] = (object) $customFields; + } + + $requestParams['Contacts'][] = (object) $contacts; + $requestParams['ContactsLists'] = $contactsLists; + + $response = HttpHelper::post($apiEndpoints, wp_json_encode($requestParams), $this->_defaultHeader); + + return $this->jobMonitoring($response); + } + + public function generateReqDataFromFieldMap($data, $fieldMap) + { + $dataFinal = []; + foreach ($fieldMap as $value) { + $triggerValue = $value->formField; + $actionValue = $value->mailjetFormField; + if ($triggerValue === 'custom') { + $dataFinal[$actionValue] = Common::replaceFieldWithValue($value->customValue, $data); + } elseif (!\is_null($data[$triggerValue])) { + $dataFinal[$actionValue] = $data[$triggerValue]; + } + } + + return $dataFinal; + } + + public function execute($selectedLists, $fieldValues, $fieldMap) + { + $finalData = $this->generateReqDataFromFieldMap($fieldValues, $fieldMap); + $apiResponse = $this->addSubscriber($selectedLists, $finalData); + + if (empty($apiResponse)) { + $res = ['message' => 'Contact ' . $this->_responseType . ' successfully']; + LogHandler::save($this->_integrationID, wp_json_encode(['type' => 'contact', 'type_name' => 'Contact ' . $this->_responseType]), 'success', wp_json_encode($res)); + } else { + LogHandler::save($this->_integrationID, wp_json_encode(['type' => '', 'type_name' => 'Adding contact']), 'error', wp_json_encode($apiResponse)); + } + + return $apiResponse; + } + + private function jobMonitoring($response) + { + $jobId = $response->Data[0]->JobID; + $apiEndpoint = 'https://api.mailjet.com/v3/REST/contact/managemanycontacts/' . $jobId; + $response = HttpHelper::get($apiEndpoint, null, $this->_defaultHeader); + + return $response->Data[0]->Error; + } + + private function isExist($email) + { + $encodedEmail = urlencode($email); + $apiEndpoint = 'https://api.mailjet.com/v3/REST/contact/' . $encodedEmail; + $response = HttpHelper::get($apiEndpoint, null, $this->_defaultHeader); + + return isset($response->Data) ? 'updated' : 'created'; + } +} diff --git a/backend/Actions/Mailjet/Routes.php b/backend/Actions/Mailjet/Routes.php new file mode 100644 index 000000000..fe0739982 --- /dev/null +++ b/backend/Actions/Mailjet/Routes.php @@ -0,0 +1,11 @@ +_integrationID = $integId; + } + + public function addSubscriber($finalData, $selectedStatus, $selectedLists, $selectedTags) + { + if (empty($finalData['email'])) { + return ['success' => false, 'message' => __('Required field Email is empty', 'bit-integrations'), 'code' => 400]; + } + + if (!empty($selectedStatus)) { + $finalData['status'] = $selectedStatus; + } + + $mailsterSubscribers = new MailsterSubscribers(); + + $subscriberAdd = $mailsterSubscribers->add($finalData); + + if (is_wp_error($subscriberAdd)) { + return $subscriberAdd; + } + + if (!empty($selectedLists)) { + $mailsterSubscribers->assign_lists($subscriberAdd, explode(',', $selectedLists)); + } + + if (!empty($selectedTags)) { + $mailsterSubscribers->assign_tags($subscriberAdd, explode(',', $selectedTags)); + } + + return $subscriberAdd; + } + + public function generateReqDataFromFieldMap($data, $fieldMap) + { + $dataFinal = []; + foreach ($fieldMap as $value) { + $triggerValue = $value->formField; + $actionValue = $value->mailsterFormField; + if ($triggerValue === 'custom') { + $dataFinal[$actionValue] = Common::replaceFieldWithValue($value->customValue, $data); + } elseif (!\is_null($data[$triggerValue])) { + $dataFinal[$actionValue] = \is_array($data[$triggerValue]) ? implode(',', $data[$triggerValue]) : $data[$triggerValue]; + } + } + + return $dataFinal; + } + + public function execute($fieldValues, $fieldMap, $selectedStatus, $selectedLists, $selectedTags) + { + $finalData = $this->generateReqDataFromFieldMap($fieldValues, $fieldMap); + $response = $this->addSubscriber($finalData, $selectedStatus, $selectedLists, $selectedTags); + + if (!is_wp_error($response)) { + $res = ['message' => __('Subscriber added successfully', 'bit-integrations')]; + LogHandler::save($this->_integrationID, wp_json_encode(['type' => 'subscriber', 'type_name' => 'Subscriber add']), 'success', wp_json_encode($res)); + } else { + LogHandler::save($this->_integrationID, wp_json_encode(['type' => '', 'type_name' => 'Adding subscriber']), 'error', wp_json_encode($response)); + } + + return $response; + } +} diff --git a/backend/Actions/Mailster/Routes.php b/backend/Actions/Mailster/Routes.php new file mode 100644 index 000000000..6ba632c34 --- /dev/null +++ b/backend/Actions/Mailster/Routes.php @@ -0,0 +1,13 @@ +_integrationDetails = $integrationDetails; + $this->_integrationID = $integId; + $this->_defaultHeader = [ + 'Authorization' => 'Bearer ' . $access_token, + 'Content-Type' => 'application/json' + ]; + } + + public function addSubscriber($selectedList, $selectedGroups, $finalData) + { + $apiEndpoints = "https://services.mailup.com/API/v1.1/Rest/ConsoleService.svc/Console/List/{$selectedList}/Recipient"; + + if (!empty($selectedGroups)) { + $apiEndpoints = "https://services.mailup.com/API/v1.1/Rest/ConsoleService.svc/Console/Group/{$selectedGroups}/Recipient"; + } + + if (!empty($this->_integrationDetails->actions->doubleOptIn)) { + $apiEndpoints = $apiEndpoints . '?ConfirmEmail=true'; + } + + if (empty($finalData['Email'])) { + return ['success' => false, 'message' => __('Required field Email is empty', 'bit-integrations'), 'code' => 400]; + } + + $requestParams = []; + $customFields = []; + + foreach ($finalData as $key => $value) { + if ($key == 'Email') { + $requestParams[$key] = $value; + } else { + $customFields[] = (object) [ + 'Id' => $key, + 'Value' => $value + ]; + } + } + + if (!empty($customFields)) { + $requestParams['Fields'] = $customFields; + } + + return HttpHelper::post($apiEndpoints, wp_json_encode($requestParams), $this->_defaultHeader); + } + + public function generateReqDataFromFieldMap($data, $fieldMap) + { + $dataFinal = []; + foreach ($fieldMap as $value) { + $triggerValue = $value->formField; + $actionValue = $value->mailupFormField; + if ($triggerValue === 'custom') { + $dataFinal[$actionValue] = Common::replaceFieldWithValue($value->customValue, $data); + } elseif (!\is_null($data[$triggerValue])) { + $dataFinal[$actionValue] = $data[$triggerValue]; + } + } + + return $dataFinal; + } + + public function execute($selectedList, $selectedGroup, $fieldValues, $fieldMap) + { + $finalData = $this->generateReqDataFromFieldMap($fieldValues, $fieldMap); + $apiResponse = $this->addSubscriber($selectedList, $selectedGroup, $finalData); + + if (\gettype($apiResponse) === 'integer') { + $res = ['message' => __('Subscriber added successfully', 'bit-integrations')]; + LogHandler::save($this->_integrationID, wp_json_encode(['type' => 'subscriber', 'type_name' => 'Subscriber added']), 'success', wp_json_encode($res)); + } else { + LogHandler::save($this->_integrationID, wp_json_encode(['type' => 'subscriber', 'type_name' => 'Adding Subscriber']), 'error', wp_json_encode($apiResponse)); + } + + return $apiResponse; + } +} diff --git a/backend/Actions/Mailup/Routes.php b/backend/Actions/Mailup/Routes.php new file mode 100644 index 000000000..b8c16442e --- /dev/null +++ b/backend/Actions/Mailup/Routes.php @@ -0,0 +1,13 @@ +get_results( $wpdb->prepare( "SELECT ID, post_title,post_content @@ -42,6 +43,7 @@ public static function getLessonByCourse($courseId) public static function getQuizByCourse($courseId) { global $wpdb; + // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Direct query needed for MasterStudy quizzes $quizzes = $wpdb->get_results( $wpdb->prepare( "SELECT ID, post_title,post_content diff --git a/backend/Actions/MasterStudyLms/RecordApiHelper.php b/backend/Actions/MasterStudyLms/RecordApiHelper.php new file mode 100644 index 000000000..c94f03751 --- /dev/null +++ b/backend/Actions/MasterStudyLms/RecordApiHelper.php @@ -0,0 +1,357 @@ +_integrationDetails = $integrationDetails; + $this->integrationID = $integId; + } + + public static function complete_course($course_id) + { + $user_id = get_current_user_id(); + if (empty($user_id)) { + return new WP_Error('REQ_FIELD_EMPTY', __('User not logged in', 'bit-integrations')); + } + $curriculum = get_post_meta($course_id, 'curriculum', true); + + if (!empty($curriculum)) { + $curriculum = STM_LMS_Helpers::only_array_numbers(explode(',', $curriculum)); + + $curriculum_posts = get_posts( + [ + 'post__in' => $curriculum, + 'posts_per_page' => 999, + 'post_type' => ['stm-lessons', 'stm-quizzes'], + 'post_status' => 'publish', + ] + ); + + if (!empty($curriculum_posts)) { + $course = stm_lms_get_user_course($user_id, $course_id, ['user_course_id']); + if (!\count($course)) { + STM_LMS_Course::add_user_course($course_id, $user_id, STM_LMS_Course::item_url($course_id, ''), 0); + STM_LMS_Course::add_student($course_id); + } + + foreach ($curriculum_posts as $post) { + if ('stm-lessons' === $post->post_type) { + $lesson_id = $post->ID; + + if (STM_LMS_Lesson::is_lesson_completed($user_id, $course_id, $lesson_id)) { + continue; + } + + $end_time = time(); + $start_time = get_user_meta($user_id, "stm_lms_course_started_{$course_id}_{$lesson_id}", true); + + stm_lms_add_user_lesson(compact('user_id', 'course_id', 'lesson_id', 'start_time', 'end_time')); + STM_LMS_Course::update_course_progress($user_id, $course_id); + + do_action('stm_lms_lesson_passed', $user_id, $lesson_id); + + delete_user_meta($user_id, "stm_lms_course_started_{$course_id}_{$lesson_id}"); + } + + if ('stm-quizzes' === $post->post_type) { + $quiz_id = $post->ID; + + if (STM_LMS_Quiz::quiz_passed($quiz_id, $user_id)) { + continue; + } + + $progress = 100; + $status = 'passed'; + $user_quiz = compact('user_id', 'course_id', 'quiz_id', 'progress', 'status'); + stm_lms_add_user_quiz($user_quiz); + stm_lms_get_delete_user_quiz_time($user_id, $quiz_id); + + STM_LMS_Course::update_course_progress($user_id, $course_id); + + $user_quiz['progress'] = round($user_quiz['progress'], 1); + do_action('stm_lms_quiz_' . $status, $user_id, $quiz_id, $user_quiz['progress']); + } + } + } + + return true; + } + + return false; + } + + public static function complete_lesson($course_id, $lesson_id) + { + $curriculum = get_post_meta($course_id, 'curriculum', true); + $user_id = get_current_user_id(); + if (!empty($curriculum)) { + $curriculum = STM_LMS_Helpers::only_array_numbers(explode(',', $curriculum)); + + $curriculum_posts = get_posts( + [ + 'post__in' => $curriculum, + 'posts_per_page' => 999, + 'post_type' => ['stm-lessons', 'stm-quizzes'], + 'post_status' => 'publish', + ] + ); + + if (!empty($curriculum_posts)) { + $curriculum = get_post_meta($course_id, 'curriculum', true); + + if (empty($curriculum)) { + return false; + } + $curriculum = explode(',', $curriculum); + + foreach ($curriculum as $item_id) { + $item_type = get_post_type($item_id); + + if ($item_type === 'stm-lessons') { + if ($item_id == $lesson_id) { + if (STM_LMS_Lesson::is_lesson_completed($user_id, $course_id, $lesson_id)) { + continue; + } + + $end_time = time(); + $start_time = get_user_meta($user_id, "stm_lms_course_started_{$course_id}_{$lesson_id}", true); + + stm_lms_add_user_lesson(compact('user_id', 'course_id', 'lesson_id', 'start_time', 'end_time')); + STM_LMS_Course::update_course_progress($user_id, $course_id); + + do_action('stm_lms_lesson_passed', $user_id, $lesson_id); + + delete_user_meta($user_id, "stm_lms_course_started_{$course_id}_{$lesson_id}"); + } + } + } + + STM_LMS_Course::update_course_progress($user_id, $course_id); + + return true; + } + + return false; + } + } + + public static function complete_quiz($course_id, $quiz_id) + { + $user_id = get_current_user_id(); + $curriculum = get_post_meta($course_id, 'curriculum', true); + + if (!empty($curriculum)) { + $curriculum = STM_LMS_Helpers::only_array_numbers(explode(',', $curriculum)); + + $curriculum_posts = get_posts( + [ + 'post__in' => $curriculum, + 'posts_per_page' => 999, + 'post_type' => ['stm-lessons', 'stm-quizzes'], + 'post_status' => 'publish', + ] + ); + + if (!empty($curriculum_posts)) { + $curriculum = get_post_meta($course_id, 'curriculum', true); + + if (empty($curriculum)) { + return false; + } + $curriculum = explode(',', $curriculum); + + foreach ($curriculum as $item_id) { + $item_type = get_post_type($item_id); + + if ($item_type === 'stm-quizzes') { + if ($item_id == $quiz_id) { + if (STM_LMS_Quiz::quiz_passed($quiz_id, $user_id)) { + continue; + } + + $progress = 100; + $status = 'passed'; + $user_quiz = compact('user_id', 'course_id', 'quiz_id', 'progress', 'status'); + stm_lms_add_user_quiz($user_quiz); + stm_lms_get_delete_user_quiz_time($user_id, $quiz_id); + + STM_LMS_Course::update_course_progress($user_id, $course_id); + + $user_quiz['progress'] = round($user_quiz['progress'], 1); + do_action('stm_lms_quiz_' . $status, $user_id, $quiz_id, $user_quiz['progress']); + } + } + } + + STM_LMS_Course::update_course_progress($user_id, $course_id); + + return true; + } + + return false; + } + } + + public static function reset_course($course_id) + { + $curriculum = get_post_meta($course_id, 'curriculum', true); + $user_id = get_current_user_id(); + + if (!empty($curriculum)) { + $curriculum = STM_LMS_Helpers::only_array_numbers(explode(',', $curriculum)); + + $curriculum_posts = get_posts( + [ + 'post__in' => $curriculum, + 'posts_per_page' => 999, + 'post_type' => ['stm-lessons', 'stm-quizzes'], + 'post_status' => 'publish', + ] + ); + + if (!empty($curriculum_posts)) { + $curriculum = get_post_meta($course_id, 'curriculum', true); + + if (empty($curriculum)) { + return false; + } + $curriculum = explode(',', $curriculum); + + foreach ($curriculum as $item_id) { + $item_type = get_post_type($item_id); + + if ($item_type === 'stm-lessons') { + // self::complete_lesson($student_id, $course_id, $item_id); + STM_LMS_User_Manager_Course_User::reset_lesson($user_id, $course_id, $item_id); + } elseif ($item_type === 'stm-assignments') { + STM_LMS_User_Manager_Course_User::reset_assignment($user_id, $course_id, $item_id); + } elseif ($item_type === 'stm-quizzes') { + STM_LMS_User_Manager_Course_User::reset_quiz($user_id, $course_id, $item_id); + } + } + + STM_LMS_Course::update_course_progress($user_id, $course_id); + + return true; + } + } + + return false; + } + + public static function reset_lesson($course_id, $lesson_id) + { + $user_id = get_current_user_id(); + $curriculum = get_post_meta($course_id, 'curriculum', true); + + if (! empty($curriculum)) { + $curriculum = STM_LMS_Helpers::only_array_numbers(explode(',', $curriculum)); + + $curriculum_posts = get_posts( + [ + 'post__in' => $curriculum, + 'posts_per_page' => 999, + 'post_type' => ['stm-lessons', 'stm-quizzes'], + 'post_status' => 'publish', + ] + ); + + if (! empty($curriculum_posts)) { + $curriculum = get_post_meta($course_id, 'curriculum', true); + + if (empty($curriculum)) { + return false; + } + $curriculum = explode(',', $curriculum); + + foreach ($curriculum as $item_id) { + $item_type = get_post_type($item_id); + + if ($item_type === 'stm-lessons') { + if ($item_id == $lesson_id) { + STM_LMS_User_Manager_Course_User::reset_lesson($user_id, $course_id, $item_id); + } + } + } + + STM_LMS_Course::update_course_progress($user_id, $course_id); + + return true; + } + + return false; + } + } + + public function execute( + $mainAction, + $fieldValues, + $integrationDetails, + $integrationData + ) { + $response = []; + $fieldData = []; + + if ($mainAction == 1) { + $courseId = $integrationDetails->courseId; + $response = self::complete_course($courseId); + if ($response) { + LogHandler::save($this->integrationID, wp_json_encode(['type' => 'course-complete', 'type_name' => 'user-course-complete']), 'success', __('Course completed successfully', 'bit-integrations')); + } else { + LogHandler::save($this->integrationID, wp_json_encode(['type' => 'course-complete', 'type_name' => 'user-course-complete']), 'error', __('Failed to completed course', 'bit-integrations')); + } + } elseif ($mainAction == 2) { + $courseId = $integrationDetails->courseId; + $lessonId = $integrationDetails->lessonId; + $response = self::complete_lesson($courseId, $lessonId); + if ($response) { + LogHandler::save($this->integrationID, wp_json_encode(['type' => 'lesson-complete', 'type_name' => 'user-lesson-complete']), 'success', __('Lesson completed successfully', 'bit-integrations')); + } else { + LogHandler::save($this->integrationID, wp_json_encode(['type' => 'lesson-complete', 'type_name' => 'user-lesson-complete']), 'error', __('Failed to completed lesson', 'bit-integrations')); + } + } elseif ($mainAction == 3) { + $courseId = $integrationDetails->courseId; + $quizId = $integrationDetails->quizId; + $response = self::complete_quiz($courseId, $quizId); + if ($response) { + LogHandler::save($this->integrationID, wp_json_encode(['type' => 'quiz-complete', 'type_name' => 'user-quiz-complete']), 'success', __('quiz completed successfully', 'bit-integrations')); + } else { + LogHandler::save($this->integrationID, wp_json_encode(['type' => 'quiz-complete', 'type_name' => 'user-quiz-complete']), 'error', __('Failed to completed quiz', 'bit-integrations')); + } + } elseif ($mainAction == 4) { + $courseId = $integrationDetails->courseId; + $response = self::reset_course($courseId); + if ($response) { + LogHandler::save($this->integrationID, wp_json_encode(['type' => 'course-reset', 'type_name' => 'user-course-reset']), 'success', __('Course reset successfully', 'bit-integrations')); + } else { + LogHandler::save($this->integrationID, wp_json_encode(['type' => 'course-reset', 'type_name' => 'user-course-reset']), 'error', __('Failed to reset course', 'bit-integrations')); + } + } elseif ($mainAction == 5) { + $course_id = $integrationDetails->courseId; + $lesson_id = $integrationDetails->lessonId; + $response = self::reset_lesson($course_id, $lesson_id); + if ($response) { + LogHandler::save($this->integrationID, wp_json_encode(['type' => 'lesson-reset', 'type_name' => 'user-lesson-reset']), 'success', __('Lesson reset successfully', 'bit-integrations')); + } else { + LogHandler::save($this->integrationID, wp_json_encode(['type' => 'lesson-reset', 'type_name' => 'user-lesson-reset']), 'error', __('Failed to reset lesson', 'bit-integrations')); + } + } + + return $response; + } +} diff --git a/backend/Actions/MasterStudyLms/Routes.php b/backend/Actions/MasterStudyLms/Routes.php new file mode 100644 index 000000000..5b6793625 --- /dev/null +++ b/backend/Actions/MasterStudyLms/Routes.php @@ -0,0 +1,13 @@ +generates_on) + (60 * 55)) < time()) { diff --git a/backend/Actions/Mautic/RecordApiHelper.php b/backend/Actions/Mautic/RecordApiHelper.php new file mode 100644 index 000000000..7fe855afd --- /dev/null +++ b/backend/Actions/Mautic/RecordApiHelper.php @@ -0,0 +1,74 @@ +_defaultHeader['Authorization'] = "Bearer {$tokenDetails->access_token}"; + $this->_defaultHeader['Content-Type'] = 'application/json'; + $this->_tokenDetails = $tokenDetails; + $this->_integrationID = $integId; + $this->_baseUrl = $baseUrl; + } + + public function insertRecord($data) + { + $data = \is_string($data) ? $data : wp_json_encode((object) $data); + $insertRecordEndpoint = "{$this->_baseUrl}/api/contacts/new"; + + return HttpHelper::post($insertRecordEndpoint, $data, $this->_defaultHeader); + } + + public function generateReqDataFromFieldMap($data, $fieldMap) + { + $dataFinal = []; + + foreach ($fieldMap as $key => $value) { + $triggerValue = $value->formField; + $actionValue = $value->mauticField; + if ($triggerValue === 'custom') { + $dataFinal[$actionValue] = Common::replaceFieldWithValue($value->customValue, $data); + } elseif (!\is_null($data[$triggerValue])) { + $dataFinal[$actionValue] = $data[$triggerValue]; + } + } + + return $dataFinal; + } + + public function execute($integrationDetails, $defaultConf, $fieldValues, $fieldMap, $actions) + { + $finalData = $this->generateReqDataFromFieldMap($fieldValues, $fieldMap); + + if (property_exists($integrationDetails, 'tag')) { + $finalData['tags'] = $integrationDetails->tag; + } + if (property_exists($integrationDetails, 'owner')) { + $finalData['owner'] = $integrationDetails->owner; + } + + $apiResponse = $this->insertRecord($finalData); + + if (isset($apiResponse->errors)) { + LogHandler::save($this->_integrationID, wp_json_encode(['type' => 'contact', 'type_name' => 'add-contact']), 'error', wp_json_encode($apiResponse)); + } else { + LogHandler::save($this->_integrationID, wp_json_encode(['type' => 'contact', 'type_name' => 'add-contact']), 'success', wp_json_encode(__('Contact Added Successfully', 'bit-integrations'))); + } + + return $apiResponse; + } +} diff --git a/backend/Actions/Mautic/Routes.php b/backend/Actions/Mautic/Routes.php new file mode 100644 index 000000000..4a87894ec --- /dev/null +++ b/backend/Actions/Mautic/Routes.php @@ -0,0 +1,14 @@ + 'memberpressproduct', 'posts_per_page' => 999, 'post_status' => 'publish', - 'meta_query' => [ + // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query -- MemberPress products are filtered by period metadata. + 'meta_query' => [ 'relation' => 'OR', [ 'key' => '_mepr_product_period_type', @@ -63,12 +66,16 @@ public function getAllMembership($label = null, $option_code = 'MPPRODUCT', $arg } wp_send_json_success($allMembership, 200); } + // translators: %s: Plugin name + // translators: %s: Placeholder value wp_send_json_error(wp_sprintf(__('%s must be activated!', 'bit-integrations'), 'Memberpress')); } public static function allPaymentGateway() { if (!self::pluginActive()) { + // translators: %s: Plugin name + // translators: %s: Placeholder value wp_send_json_error(wp_sprintf(__('%s must be activated!', 'bit-integrations'), 'Memberpress')); } $mepr_options = MeprOptions::fetch(); @@ -104,6 +111,9 @@ public function execute($integrationData, $fieldValues) empty($integId) || empty($mainAction) || empty($selectedMembership) ) { + // translators: %s: Integration name + + // translators: %s: Placeholder value return new WP_Error('REQ_FIELD_EMPTY', wp_sprintf(__('module, fields are required for %s api', 'bit-integrations'), 'memberpress')); } $recordApiHelper = new RecordApiHelper($integrationDetails, $integId); diff --git a/backend/Actions/Memberpress/RecordApiHelper.php b/backend/Actions/Memberpress/RecordApiHelper.php new file mode 100644 index 000000000..cc4f0bee3 --- /dev/null +++ b/backend/Actions/Memberpress/RecordApiHelper.php @@ -0,0 +1,181 @@ +_integrationDetails = $integrationDetails; + self::$integrationID = $integId; + } + + public function generateReqDataFromFieldMap($data, $fieldMap) + { + $dataFinal = []; + + foreach ($fieldMap as $key => $value) { + $triggerValue = $value->formField; + $actionValue = $value->memberpressFormField; + if ($triggerValue === 'custom') { + $dataFinal[$actionValue] = Common::replaceFieldWithValue($value->customValue, $data); + } elseif (!\is_null($data[$triggerValue])) { + $dataFinal[$actionValue] = $data[$triggerValue]; + } + } + + return $dataFinal; + } + + public function crateMember($integrationDetails, $finalData) + { + $statusId = $integrationDetails->statusId; + $gateway = $integrationDetails->gatewayId; + $user_id = get_current_user_id(); + + $product_id = $integrationDetails->selectedMembership; + $allData = new MeprTransaction(); + $user = new MeprUser(); + $user->load_user_data_by_id($user_id); + + $allData->user_id = $user->ID; + $allData->product_id = sanitize_key($product_id); + + $allData->amount = (float) $finalData['sub_total'] ?? 0; + $allData->tax_amount = (float) $finalData['tax_amount'] ?? 0; + $allData->tax_rate = (float) $finalData['taxrate'] ?? 0; + + $allData->total = ($allData->amount + $allData->tax_amount); + $allData->status = sanitize_text_field($statusId); + $allData->gateway = sanitize_text_field($gateway); + $allData->created_at = MeprUtils::ts_to_mysql_date(time()); + + $expiration_date = $finalData['expiration_date'] ?? null; + + if (empty($expiration_date)) { + $obj = new MeprProduct(sanitize_key($product_id)); + $expires_at_ts = $obj->get_expires_at(); + if (\is_null($expires_at_ts)) { + $allData->expires_at = MeprUtils::db_lifetime(); + } else { + $allData->expires_at = MeprUtils::ts_to_mysql_date($expires_at_ts, 'Y-m-d 23:59:59'); + } + } else { + $allData->expires_at = MeprUtils::ts_to_mysql_date(strtotime($expiration_date), 'Y-m-d 23:59:59'); + } + + $apiResponse = $allData->store(); + + if ($allData->status == MeprTransaction::$complete_str) { + MeprEvent::record('transaction-completed', $allData); + + // This is a recurring payment + if (($sub = $allData->subscription()) && $sub->txn_count > 1) { + MeprEvent::record( + 'recurring-transaction-completed', + $allData + ); + } elseif (!$sub) { + MeprEvent::record( + 'non-recurring-transaction-completed', + $allData + ); + } + } + + return $apiResponse; + } + + public function removeUserFormMembership($integrationDetails, $finalData) + { + $membership = $integrationDetails->selectedMembership; + $user_id = get_current_user_id(); + $user_obj = get_user_by('id', $user_id); + $table = MeprSubscription::account_subscr_table( + 'created_at', + 'DESC', + '', + '', + 'any', + '', + false, + [ + 'member' => $user_obj->user_login, + 'statuses' => [ + MeprSubscription::$active_str, + MeprSubscription::$suspended_str, + MeprSubscription::$cancelled_str, + ], + ], + MeprHooks::apply_filters( + 'mepr_user_subscriptions_query_cols', + [ + 'id', + 'product_id', + 'created_at', + ] + ) + ); + + if ($table['count'] > 0) { + foreach ($table['results'] as $row) { + if ($row->product_id == $membership || $membership == - 1) { + if ($row->sub_type == 'subscription') { + $sub = new MeprSubscription($row->id); + } elseif ($row->sub_type == 'transaction') { + $sub = new MeprTransaction($row->id); + } + $apiResponse = $sub->destroy(); + $member = $sub->user(); + $member->update_member_data(); + } + } + + return $apiResponse; + } + } + + public function execute( + $mainAction, + $fieldValues, + $fieldMap, + $integrationDetails + ) { + $fieldData = []; + $apiResponse = null; + if ($mainAction === '1') { + $finalData = $this->generateReqDataFromFieldMap($fieldValues, $fieldMap); + $apiResponse = $this->crateMember($integrationDetails, $finalData); + if (!empty($apiResponse) && \gettype($apiResponse) !== 'integer') { + LogHandler::save(self::$integrationID, wp_json_encode(['type' => 'add user', 'type_name' => 'Add the user to a membership']), 'error', wp_json_encode(__('Failed to add user to membership', 'bit-integrations'))); + } else { + // translators: %s: Placeholder value + LogHandler::save(self::$integrationID, wp_json_encode(['type' => 'add user', 'type_name' => 'Add the user to a membership']), 'success', wp_json_encode(wp_sprintf(__('Successfully user added to the membership and id is: %s', 'bit-integrations'), $apiResponse))); + } + } elseif ($mainAction === '2') { + $finalData = $this->generateReqDataFromFieldMap($fieldValues, $fieldMap); + $apiResponse = $this->removeUserFormMembership($integrationDetails, $finalData); + if ($apiResponse) { + LogHandler::save(self::$integrationID, wp_json_encode(['type' => 'add user', 'type_name' => 'Add the user to a membership']), 'success', wp_json_encode(__('Successfully user removed form membership', 'bit-integrations'))); + } else { + LogHandler::save(self::$integrationID, wp_json_encode(['type' => 'remove user', 'type_name' => 'Remove user to a membership']), 'error', wp_json_encode(__('Failed to remove user form membership', 'bit-integrations'))); + } + } + + return $apiResponse; + } +} diff --git a/backend/Actions/Memberpress/Routes.php b/backend/Actions/Memberpress/Routes.php new file mode 100644 index 000000000..d47ee3130 --- /dev/null +++ b/backend/Actions/Memberpress/Routes.php @@ -0,0 +1,12 @@ +_integrationDetails = $integrationDetails; + $this->_integrationID = $integId; + } + + public function generateReqDataFromFieldMap($data, $field_map) + { + foreach ($field_map as $key => $value) { + $triggerValue = $value->formFields; + $actionValue = $value->moosendFormFields; + + $formattedValue = isset($data[$triggerValue]) ? MoosendHelper::formatPhoneNumber($data[$triggerValue]) : null; + + if (strpos($actionValue, 'custom_field_') !== 0) { + $dataFinal[$actionValue] = ($triggerValue === 'custom') + ? Common::replaceFieldWithValue($value->customValue, $data) + : $formattedValue; + } + } + + $filtered = Hooks::apply(Config::withPrefix('moosend_map_custom_fields'), $dataFinal, $data, $field_map); + + /** + * @deprecated 2.7.8 Use `bit_integrations_moosend_map_custom_fields` filter instead. + * @since 2.7.8 + */ + $filtered = Hooks::apply('btcbi_moosend_map_custom_fields', $filtered, $data, $field_map); + + return $filtered; + } + + public function response($status, $code, $type, $typeName, $apiResponse) + { + $res = ['success' => $code === 200 ? true : false, 'message' => $apiResponse, 'code' => $code]; + LogHandler::save($this->_integrationID, wp_json_encode(['type' => $type, 'type_name' => $typeName]), $status, wp_json_encode($res)); + + return $res; + } + + public function subscribe($authKey, $listId, $formData) + { + $apiEndpoints = "{$this->baseUrl}subscribers/{$listId}/subscribe.json?apikey={$authKey}"; + $headers = ['Content-Type' => 'application/json']; + + return HttpHelper::post($apiEndpoints, wp_json_encode($formData), $headers); + } + + public function unsubscribe($authKey, $formData) + { + $apiEndpoints = "{$this->baseUrl}subscribers/unsubscribe.json?apikey={$authKey}"; + $headers = [ + 'Content-Type' => 'application/json' + ]; + + return HttpHelper::post($apiEndpoints, wp_json_encode($formData), $headers); + } + + public function unsubscribeFromList($authKey, $listId, $formData) + { + $apiEndpoints = "{$this->baseUrl}subscribers/{$listId}/unsubscribe.json?apikey={$authKey}"; + $headers = [ + 'Content-Type' => 'application/json' + ]; + + return HttpHelper::post($apiEndpoints, wp_json_encode($formData), $headers); + } + + public function execute( + $listId, + $method, + $fieldValues, + $field_map, + $authKey + ) { + $finalData = (object) $this->generateReqDataFromFieldMap($fieldValues, $field_map); + + switch ($method) { + case 1: + $apiResponse = $this->subscribe($authKey, $listId, $finalData); + if ($apiResponse->Code !== 0) { + $this->response('error', 400, 'subscribe', 'Active-contact', $apiResponse); + } else { + $this->response('success', 200, 'subscribe', 'Active-contact', $apiResponse); + } + + break; + case 2: + $apiResponse = $this->unsubscribeFromList($authKey, $listId, $finalData); + if ($apiResponse->Code === 300) { + $this->response('error', 400, 'unsubscribe', 'unsubscribe-from-list', $apiResponse); + } else { + $this->response('success', 200, 'unsubscribe', 'unsubscribe-from-list', $apiResponse); + } + + break; + case 0: + $apiResponse = $this->unsubscribe($authKey, $finalData); + if ($apiResponse->Code === 300) { + $this->response('error', 400, 'unsubscribe', 'unsubscribe-contact', $apiResponse); + } else { + $this->response('success', 200, 'unsubscribe', 'unsubscribe-contact', $apiResponse); + } + + break; + } + + return $apiResponse; + } + + private static function formatPhoneNumber($field) + { + if (!preg_match('/^\+?[0-9\s\-\(\)]+$/', $field)) { + return $field; + } + + $leadingPlus = $field[0] === '+' ? '+' : ''; + $cleanedNumber = preg_replace('/[^\d]/', '', $field); + $formattedDigits = trim(chunk_split($cleanedNumber, 3, ' ')); + + return $leadingPlus . $formattedDigits; + } +} diff --git a/backend/Actions/Moosend/Routes.php b/backend/Actions/Moosend/Routes.php new file mode 100644 index 000000000..10bfc550d --- /dev/null +++ b/backend/Actions/Moosend/Routes.php @@ -0,0 +1,10 @@ +actionName; if (empty($fieldMap) || empty($authToken) || empty($actionName)) { + // translators: %s: Placeholder value return new WP_Error('REQ_FIELD_EMPTY', wp_sprintf(__('module, fields are required for %s api', 'bit-integrations'), 'MoxieCRM')); } diff --git a/backend/Actions/MoxieCRM/RecordApiHelper.php b/backend/Actions/MoxieCRM/RecordApiHelper.php new file mode 100644 index 000000000..6ac155506 --- /dev/null +++ b/backend/Actions/MoxieCRM/RecordApiHelper.php @@ -0,0 +1,180 @@ +integrationDetails = $integrationDetails; + $this->integrationId = $integId; + $this->apiUrl = $integrationDetails->api_url; + $this->defaultHeader = [ + 'X-API-KEY' => $integrationDetails->api_key, + 'Content-Type' => 'application/json' + ]; + } + + public function addClient($finalData) + { + if (empty($finalData['name'])) { + return ['success' => false, 'message' => __('Required field Name is empty', 'bit-integrations'), 'code' => 400]; + } + + $staticFieldsKeys = ['name', 'address1', 'address2', 'city', 'locality', 'postal', 'country', 'website', 'phone', 'leadSource', 'hourlyAmount', 'currency', 'notes', 'firstName', 'lastName', 'email']; + $contacts = []; + foreach ($finalData as $key => $value) { + if ($key == 'firstName' || $key == 'lastName' || $key == 'email') { + $contacts[$key] = $value; + } else { + $requestParams[$key] = $value; + } + } + $requestParams['contacts'][] = $contacts; + + if ($this->integrationDetails->recordType) { + $requestParams['clientType'] = $this->integrationDetails->recordType; + } + + $this->type = 'Client'; + $this->typeName = 'Client created'; + + $apiEndpoint = 'https://' . $this->apiUrl . '/api/public/action/clients/create'; + + return $response = HttpHelper::post($apiEndpoint, wp_json_encode($requestParams), $this->defaultHeader); + } + + public function addContact($finalData) + { + if (empty($finalData['email'])) { + return ['success' => false, 'message' => __('Required field Email is empty', 'bit-integrations'), 'code' => 400]; + } + + $staticFieldsKeys = ['email', 'first', 'last', 'phone', 'notes']; + + foreach ($finalData as $key => $value) { + if (\in_array($key, $staticFieldsKeys)) { + $requestParams[$key] = $value; + } else { + $requestParams['customValues'][] = (object) [ + 'Custom Field' => $value, + 'custom_field_definition_id' => $key + ]; + } + } + + if ($this->integrationDetails->actions->client) { + $requestParams['clientName'] = $this->integrationDetails->selectedClient; + } + + $this->type = 'Contact'; + $this->typeName = 'Contact created'; + + $apiEndpoint = 'https://' . $this->apiUrl . '/api/public/action/contacts/create'; + + return $response = HttpHelper::post($apiEndpoint, wp_json_encode($requestParams), $this->defaultHeader); + } + + public function addOpportunity($finalData) + { + if (empty($finalData['name'])) { + return ['success' => false, 'message' => __('Required field opportunity name is empty', 'bit-integrations'), 'code' => 400]; + } + $staticFieldsKeys = ['name', 'description', 'value', 'firstName', 'lastName', 'email', 'phone', 'role', 'businessName', 'website', 'address1', 'address2', 'city', 'locality', 'postal', 'country', 'sourceUrl', 'leadSource']; + + foreach ($finalData as $key => $value) { + if (\in_array($key, $staticFieldsKeys)) { + if (($key == 'firstName' || $key == 'lastName' || $key == 'email' || $key == 'phone' || $key == 'role' || $key == 'businessName' || $key == 'website' || $key == 'address1' || $key == 'address2' || $key == 'city' || $key == 'locality' || $key == 'postal' || $key == 'country' || $key == 'sourceUrl' || $key == 'leadSource')) { + $requestParams['leadInfo'][$key] = $value; + } else { + $requestParams[$key] = $value; + } + } else { + $requestParams['customValues'][] = (object) [ + 'Custom Field' => $value, + ]; + } + } + + if ($this->integrationDetails->actions->client) { + $requestParams['clientName'] = ($this->integrationDetails->selectedClient); + } + if (!empty($this->integrationDetails->actions->pipelineStage)) { + $requestParams['stageName'] = ($this->integrationDetails->selectedPipelineStage); + } + + $this->type = 'Opportunity'; + $this->typeName = 'Opportunity created'; + + $apiEndpoint = 'https://' . $this->apiUrl . '/api/public/action/opportunities/create'; + + return $response = HttpHelper::post($apiEndpoint, wp_json_encode($requestParams), $this->defaultHeader); + } + + public function generateReqDataFromFieldMap($data, $fieldMap) + { + $dataFinal = []; + foreach ($fieldMap as $value) { + $triggerValue = $value->formField; + $actionValue = $value->moxiecrmFormField; + if ($triggerValue === 'custom') { + if ($actionValue === 'customValues') { + $dataFinal[$value->customFieldKey] = $value->customValue; + } else { + $dataFinal[$actionValue] = $value->customValue; + } + } elseif (!\is_null($data[$triggerValue])) { + if ($actionValue === 'customValues') { + $dataFinal[$value->customFieldKey] = $data[$triggerValue]; + } else { + $dataFinal[$actionValue] = $data[$triggerValue]; + } + } + } + + return $dataFinal; + } + + public function execute($fieldValues, $fieldMap, $actionName) + { + $finalData = $this->generateReqDataFromFieldMap($fieldValues, $fieldMap); + if ($actionName === 'client') { + $apiResponse = $this->addClient($finalData); + } elseif ($actionName === 'contact') { + $apiResponse = $this->addContact($finalData); + } elseif ($actionName === 'opportunity') { + $apiResponse = $this->addOpportunity($finalData); + } + + if (isset($apiResponse->id) || $apiResponse->status === 'success') { + $res = [$this->typeName . ' successfully']; + LogHandler::save($this->integrationId, wp_json_encode(['type' => $this->type, 'type_name' => $this->typeName]), 'success', wp_json_encode($res)); + } else { + LogHandler::save($this->integrationId, wp_json_encode(['type' => $this->type, 'type_name' => $this->type . ' creating']), 'error', wp_json_encode($apiResponse)); + } + + return $apiResponse; + } +} diff --git a/backend/Actions/MoxieCRM/Routes.php b/backend/Actions/MoxieCRM/Routes.php new file mode 100644 index 000000000..70192e20b --- /dev/null +++ b/backend/Actions/MoxieCRM/Routes.php @@ -0,0 +1,16 @@ +_integrationID = $integId; + } + + public function addSubscriber($finalData, $selectedLists) + { + if (empty($finalData['email'])) { + return ['success' => false, 'message' => __('Required field Email is empty', 'bit-integrations'), 'code' => 400]; + } + + if (!empty($selectedLists)) { + $finalData['lists'] = explode(',', $selectedLists); + } + + return TNP::add_subscriber($finalData); + } + + public function generateReqDataFromFieldMap($data, $fieldMap) + { + $dataFinal = []; + foreach ($fieldMap as $value) { + $triggerValue = $value->formField; + $actionValue = $value->newsletterFormField; + if ($triggerValue === 'custom') { + $dataFinal[$actionValue] = Common::replaceFieldWithValue($value->customValue, $data); + } elseif (!\is_null($data[$triggerValue])) { + $dataFinal[$actionValue] = $data[$triggerValue]; + } + } + + return $dataFinal; + } + + public function execute($fieldValues, $fieldMap, $selectedLists) + { + $finalData = $this->generateReqDataFromFieldMap($fieldValues, $fieldMap); + $response = $this->addSubscriber($finalData, $selectedLists); + + if (isset($response->id)) { + $res = ['message' => __('Subscriber added successfully', 'bit-integrations')]; + LogHandler::save($this->_integrationID, wp_json_encode(['type' => 'subscriber', 'type_name' => 'Subscriber add']), 'success', wp_json_encode($res)); + } else { + LogHandler::save($this->_integrationID, wp_json_encode(['type' => '', 'type_name' => 'Adding subscriber']), 'error', wp_json_encode($response)); + } + + return $response; + } +} diff --git a/backend/Actions/Newsletter/Routes.php b/backend/Actions/Newsletter/Routes.php new file mode 100644 index 000000000..797f4cea0 --- /dev/null +++ b/backend/Actions/Newsletter/Routes.php @@ -0,0 +1,10 @@ +actionName; if (empty($fieldMap) || empty($apiKey) || empty($actionName)) { + // translators: %s: Placeholder value return new WP_Error('REQ_FIELD_EMPTY', wp_sprintf(__('module, fields are required for %s api', 'bit-integrations'), 'Nimble')); } diff --git a/backend/Actions/Nimble/RecordApiHelper.php b/backend/Actions/Nimble/RecordApiHelper.php new file mode 100644 index 000000000..ec590cfba --- /dev/null +++ b/backend/Actions/Nimble/RecordApiHelper.php @@ -0,0 +1,128 @@ +integrationDetails = $integrationDetails; + $this->integrationId = $integId; + $this->apiUrl = 'https://app.nimble.com/api/v1'; + $this->defaultHeader = [ + 'Authorization' => "Bearer {$apiKey}", + 'Accept' => 'application/json', + 'Content-Type' => 'application/json' + ]; + } + + public function addContact($finalData, $actionName) + { + $fieldData = []; + $modifierField = ['Email', 'Phone', 'Address', 'URL']; + $this->type = $actionName === 'person' ? 'People' : 'Company'; + $this->typeName = $actionName === 'person' ? 'People' : 'Company' . ' Added'; + + if ($actionName === 'person' && empty($finalData['first name'])) { + return ['success' => false, 'message' => __('Required field First Name is empty', 'bit-integrations'), 'code' => 400]; + } + if ($actionName === 'company' && empty($finalData['company name'])) { + return ['success' => false, 'message' => __('Required field Company Name is empty', 'bit-integrations'), 'code' => 400]; + } + + foreach ($finalData as $key => $value) { + $fieldData[$key] = $this->setFieldObj($key, $value, $modifierField); + } + if ($actionName === 'company' && isset($this->integrationDetails->selectedxofEmployees) && !empty($this->integrationDetails->selectedxofEmployees)) { + $fieldData['# of employees'] = $this->setFieldObj('# of employees', $this->integrationDetails->selectedxofEmployees, $modifierField); + } + + $fieldData = array_merge($fieldData, $this->setActionsField($modifierField)); + $dataFinal = [ + 'record_type' => $actionName === 'person' ? 'person' : 'company', + 'fields' => (object) $fieldData + ]; + + $apiEndpoint = $this->apiUrl . '/contact'; + + return HttpHelper::post($apiEndpoint, wp_json_encode($dataFinal), $this->defaultHeader); + } + + public function generateReqDataFromFieldMap($data, $fieldMap) + { + $dataFinal = []; + foreach ($fieldMap as $value) { + $triggerValue = $value->formField; + $actionValue = $value->nimbleFormField; + $dataFinal[$actionValue] = $triggerValue === 'custom' ? $value->customValue : $data[$triggerValue]; + } + + return $dataFinal; + } + + public function execute($fieldValues, $fieldMap, $actionName) + { + $finalData = $this->generateReqDataFromFieldMap($fieldValues, $fieldMap); + $apiResponse = $this->addContact($finalData, $actionName); + + if (isset($apiResponse->id)) { + $res = [$this->typeName . ' successfully']; + LogHandler::save($this->integrationId, wp_json_encode(['type' => $this->type, 'type_name' => $this->typeName]), 'success', wp_json_encode($res)); + } else { + LogHandler::save($this->integrationId, wp_json_encode(['type' => $this->type, 'type_name' => $this->type . ' creating']), 'error', wp_json_encode($apiResponse)); + } + + return $apiResponse; + } + + private function setFieldObj($key, $value, $modifierField) + { + return [ + (object) [ + 'value' => $value, + 'modifier' => \in_array($key, $modifierField) ? 'work' : '' + ] + ]; + } + + private function setActionsField() + { + $actionFields = []; + if (isset($this->integrationDetails->selectedRating) && !empty($this->integrationDetails->selectedRating)) { + $actionFields['rating'] = $this->setFieldObj('rating', $this->integrationDetails->selectedRating, []); + } + if (isset($this->integrationDetails->selectedLeadStatus) && !empty($this->integrationDetails->selectedLeadStatus)) { + $actionFields['lead status'] = $this->setFieldObj('lead status', $this->integrationDetails->selectedLeadStatus, []); + } + if (isset($this->integrationDetails->selectedLeadSource) && !empty($this->integrationDetails->selectedLeadSource)) { + $actionFields['lead source'] = $this->setFieldObj('lead source', $this->integrationDetails->selectedLeadSource, []); + } + if (isset($this->integrationDetails->selectedLeadType) && !empty($this->integrationDetails->selectedLeadType)) { + $actionFields['lead type'] = $this->setFieldObj('lead type', $this->integrationDetails->selectedLeadType, []); + } + + return $actionFields; + } +} diff --git a/backend/Actions/Nimble/Routes.php b/backend/Actions/Nimble/Routes.php new file mode 100644 index 000000000..326fdc406 --- /dev/null +++ b/backend/Actions/Nimble/Routes.php @@ -0,0 +1,12 @@ +_integrationDetails = $integrationDetails; + $this->_integrationID = $integId; + } + + public function generateReqDataFromFieldMap($data, $field_map) + { + $dataFinal = []; + + foreach ($field_map as $key => $value) { + $triggerValue = $value->formFields; + $actionValue = $value->notionFormFields; + if ($triggerValue === 'custom') { + $dataFinal[$actionValue] = Common::replaceFieldWithValue($value->customValue, $data); + } elseif (!\is_null($value->customValue)) { + $dataFinal[$actionValue] = $value->customValue; + } elseif (!\is_null($data[$triggerValue])) { + $dataFinal[$actionValue] = $data[$triggerValue]; + } + } + + return $dataFinal; + } + + public function response($status, $code, $type, $typeName, $apiResponse) + { + $res = ['success' => $code === 200 ? true : false, 'message' => $apiResponse, 'code' => $code]; + LogHandler::save($this->_integrationID, wp_json_encode(['type' => $type, 'type_name' => $typeName]), $status, wp_json_encode($res)); + + return $res; + } + + public function createItemInDatabase($databaseId, $tokenType, $accessToken, $data) + { + $apiEndpoints = "{$this->baseUrl}pages"; + $headers = [ + 'Authorization' => ucfirst($tokenType) . ' ' . $accessToken, + 'Content-Type' => 'application/json', + 'Notion-Version' => '2022-06-28' + ]; + $parent = [ + 'type' => 'database_id', + 'database_id' => $databaseId + ]; + + $body = [ + 'parent' => $parent, + 'properties' => $data + ]; + + return HttpHelper::post($apiEndpoints, wp_json_encode($body), $headers); + } + + public function valueFormat($type, $value) + { + switch ($type) { + case 'number': + return (int) $value; + + break; + case 'date': + return ['start' => Helper::formatToISO8601($value)]; + case 'checkbox': + return settype($value, 'boolean'); + case 'select': + if (\is_array($value)) { + return ['name' => $value[0]]; + } + + return ['name' => $value]; + case 'multi_select': + $data = []; + foreach ($value as $key => $value) { + $data[] = ['name' => $value]; + } + + return $data; + case 'status': + return ['name' => $value]; + case 'files': + $files = []; + + if (\is_array($value)) { + foreach ($value as $file) { + $files[] = self::setFile($file); + } + } else { + $files[] = self::setFile($value); + } + + return $files; + default: + return $value; + } + } + + public function execute( + $databaseId, + $accessToken, + $tokenType, + $notionFields, + $fieldValues, + $field_map + ) { + $finalData = $this->generateReqDataFromFieldMap($fieldValues, $field_map); + $result = []; + + foreach ($finalData as $formKey => $formValue) { + foreach ($notionFields as $key => $value) { + if ($value->label === $formKey) { + if ($value->key == 'rich_text' || $value->key == 'title') { + $result[$formKey] = [$value->key => [['text' => ['content' => $formValue]]]]; + } else { + $result[$formKey] = [ + $value->key => $this->valueFormat($value->key, $formValue) + ]; + } + } + } + } + + $apiResponse = $this->createItemInDatabase($databaseId, $tokenType, $accessToken, $result); + if ($apiResponse->object === 'error') { + $apiResponse = $this->response('error', 400, 'database', 'create item', $apiResponse); + } else { + $apiResponse = $this->response('success', 200, 'database', 'create item', $apiResponse); + } + + return $apiResponse; + } + + private static function setFile($file) + { + $dir = wp_upload_dir(); + + return [ + 'name' => 'media', + 'type' => 'external', + 'external' => [ + 'url' => trim(str_replace($dir['basedir'], $dir['baseurl'], $file)) + ] + ]; + } +} diff --git a/backend/Actions/Notion/Routes.php b/backend/Actions/Notion/Routes.php new file mode 100644 index 000000000..c38d40e04 --- /dev/null +++ b/backend/Actions/Notion/Routes.php @@ -0,0 +1,12 @@ +actionName; if (empty($fieldMap) || empty($userName) || empty($apiToken) || empty($actionName)) { + // translators: %s: Placeholder value return new WP_Error('REQ_FIELD_EMPTY', wp_sprintf(__('module, fields are required for %s api', 'bit-integrations'), 'NutshellCRM')); } diff --git a/backend/Actions/NutshellCRM/RecordApiHelper.php b/backend/Actions/NutshellCRM/RecordApiHelper.php new file mode 100644 index 000000000..2f38f5871 --- /dev/null +++ b/backend/Actions/NutshellCRM/RecordApiHelper.php @@ -0,0 +1,229 @@ +integrationDetails = $integrationDetails; + $this->integrationId = $integId; + $this->apiUrl = 'https://app.nutshell.com/api/v1/json'; + $this->defaultHeader = [ + 'Authorization' => 'Basic ' . base64_encode("{$userName}:{$apiToken}"), + 'Content-type' => 'application/json', + ]; + } + + public function addPeople($finalData) + { + if (empty($finalData['first_name'] || $finalData['email'])) { + return ['success' => false, 'message' => __('Required field First Name or Email is empty', 'bit-integrations'), 'code' => 400]; + } + + $staticFieldsKeys = ['first_name', 'email', 'last_name', 'phone', 'address_1', 'city', 'state', 'postalCode', 'country']; + + foreach ($finalData as $key => $value) { + if (\in_array($key, $staticFieldsKeys)) { + if (($key == 'address_1' || $key == 'city' || $key == 'state' || $key == 'postalCode' || $key == 'country')) { + $requestParams['address'][$key] = $value; + } elseif ($key == 'first_name') { + $requestParams['name']['givenName'] = $value; + } elseif ($key == 'last_name') { + $requestParams['name']['familyName'] = $value; + } else { + $requestParams[$key] = $value; + } + } else { + $requestParams['customFields'][] = (object) [ + $key => $value, + ]; + } + } + + if ($this->integrationDetails->actions->Company) { + $requestParams['accounts'][] = (object) [ + 'id' => ($this->integrationDetails->selectedCompany) + ]; + } + + $this->type = 'People'; + $this->typeName = 'People created'; + + $body = [ + 'method' => 'newContact', + 'id' => 'randomstring', + 'params' => (object) [ + 'contact' => $requestParams + ] + ]; + + $apiEndpoint = $this->apiUrl; + + return HttpHelper::post($apiEndpoint, wp_json_encode($body), $this->defaultHeader); + } + + public function addCompany($finalData) + { + if (empty($finalData['name'])) { + return ['success' => false, 'message' => __('Required field Full Name is empty', 'bit-integrations'), 'code' => 400]; + } + + $staticFieldsKeys = ['name', 'url', 'phone', 'address_1', 'city', 'state', 'postalCode', 'country']; + + foreach ($finalData as $key => $value) { + if (\in_array($key, $staticFieldsKeys)) { + if (($key == 'address_1' || $key == 'city' || $key == 'state' || $key == 'postalCode' || $key == 'country')) { + $requestParams['address'][$key] = $value; + } else { + $requestParams[$key] = $value; + } + } else { + $requestParams['customFields'][] = (object) [ + $key => $value, + ]; + } + } + + if ($this->integrationDetails->actions->Contact) { + $requestParams['contacts'][] = (object) [ + 'id' => ($this->integrationDetails->selectedContact) + ]; + } + if ($this->integrationDetails->actions->CompanyType) { + $requestParams['accountTypeId'] = ($this->integrationDetails->selectedCompanyType); + } + + $this->type = 'Company'; + $this->typeName = 'Company created'; + + $body = [ + 'method' => 'newAccount', + 'id' => 'randomstring', + 'params' => (object) [ + 'account' => $requestParams + ] + ]; + + $apiEndpoint = $this->apiUrl; + + return HttpHelper::post($apiEndpoint, wp_json_encode($body), $this->defaultHeader); + } + + public function addLead($finalData) + { + if (empty($finalData['description'])) { + return ['success' => false, 'message' => __('Required field Description is empty', 'bit-integrations'), 'code' => 400]; + } + + $staticFieldsKeys = ['description', 'dueTime', 'confidence']; + + foreach ($finalData as $key => $value) { + if (\in_array($key, $staticFieldsKeys)) { + $requestParams[$key] = $value; + } else { + $requestParams['customFields'][] = (object) [ + $key => $value, + ]; + } + } + + if ($this->integrationDetails->actions->Contact) { + $requestParams['contacts'][] = (object) [ + 'id' => ($this->integrationDetails->selectedContact) + ]; + } + if ($this->integrationDetails->actions->Company) { + $requestParams['accounts'][] = (object) [ + 'id' => ($this->integrationDetails->selectedCompany) + ]; + } + if ($this->integrationDetails->actions->Product) { + $requestParams['products'][] = (object) [ + 'id' => ($this->integrationDetails->selectedProduct) + ]; + } + if ($this->integrationDetails->actions->Source) { + $requestParams['sources'][] = (object) [ + 'id' => ($this->integrationDetails->selectedSource) + ]; + } + if ($this->integrationDetails->actions->Tag) { + $requestParams['tags'][] = ($this->integrationDetails->selectedTag); + } + + if ($this->integrationDetails->actions->Priority) { + $requestParams['priority'] = (int) ($this->integrationDetails->actions->Priority); + } + + $this->type = 'Lead'; + $this->typeName = 'Lead created'; + + $body = [ + 'method' => 'newLead', + 'id' => 'randomstring', + 'params' => (object) [ + 'lead' => $requestParams + ] + ]; + + $apiEndpoint = $this->apiUrl; + + return HttpHelper::post($apiEndpoint, wp_json_encode($body), $this->defaultHeader); + } + + public function generateReqDataFromFieldMap($data, $fieldMap) + { + $dataFinal = []; + foreach ($fieldMap as $value) { + $triggerValue = $value->formField; + $actionValue = $value->nutshellCRMFormField; + $dataFinal[$actionValue] = ($triggerValue === 'custom') ? $value->customValue : $data[$triggerValue]; + } + + return $dataFinal; + } + + public function execute($fieldValues, $fieldMap, $actionName) + { + $finalData = $this->generateReqDataFromFieldMap($fieldValues, $fieldMap); + if ($actionName === 'people') { + $apiResponse = $this->addPeople($finalData); + } elseif ($actionName === 'company') { + $apiResponse = $this->addCompany($finalData); + } elseif ($actionName === 'lead') { + $apiResponse = $this->addLead($finalData); + } + + if (isset($apiResponse->result)) { + $res = [$this->typeName . ' successfully']; + LogHandler::save($this->integrationId, wp_json_encode(['type' => $this->type, 'type_name' => $this->typeName]), 'success', wp_json_encode($res)); + } else { + LogHandler::save($this->integrationId, wp_json_encode(['type' => $this->type, 'type_name' => $this->type . ' creating']), 'error', wp_json_encode($apiResponse)); + } + + return $apiResponse; + } +} diff --git a/backend/Actions/NutshellCRM/Routes.php b/backend/Actions/NutshellCRM/Routes.php new file mode 100644 index 000000000..6ff9d9a30 --- /dev/null +++ b/backend/Actions/NutshellCRM/Routes.php @@ -0,0 +1,16 @@ +_integrationDetails = $integrationDetails; + $this->_integrationID = $integId; + $this->_defaultHeader = [ + 'X-API-KEY' => $this->_integrationDetails->api_key + ]; + } + + public function addContact( + $channels, + $emailStatus, + $smsStatus, + $finalData, + $customProperties + ) { + $apiEndpoints = $this->baseUrl . 'contacts'; + $splitChannels = []; + if (!empty($channels)) { + $splitChannels = explode(',', $channels); + } else { + return ['success' => false, 'message' => __('At least one channel is required', 'bit-integrations'), 'code' => 400]; + } + $email = $finalData['email']; + $phone = $finalData['phone_number']; + + $identifires = []; + if (\count($splitChannels)) { + foreach ($splitChannels as $channel) { + $status = $channel === 'email' ? $emailStatus : $smsStatus; + $type = $channel === 'email' ? 'email' : 'phone'; + $id = $channel === 'email' ? $email : $phone; + $identifires[] = (object) [ + 'channels' => [ + $channel => [ + 'status' => $status + ] + ], + 'type' => $type, + 'id' => $id + + ]; + } + } + + $requestParams['identifiers'] = $identifires; + $requestParams['customProperties'] = $customProperties; + + if ($this->_integrationDetails->actions->tag && !empty($this->_integrationDetails->selected_tags)) { + $requestParams['tags'] = explode(',', $this->_integrationDetails->selected_tags); + } + + foreach ($finalData as $key => $value) { + if ($key !== 'email' && $key !== 'phone_number') { + $requestParams[$key] = $value; + } + } + + return HttpHelper::post($apiEndpoints, wp_json_encode($requestParams), $this->_defaultHeader); + } + + public function generateReqDataFromFieldMap($data, $fieldMap) + { + $dataFinal = []; + foreach ($fieldMap as $key => $value) { + $triggerValue = $value->formField; + $actionValue = $value->omniSendFormField; + if ($triggerValue === 'custom') { + $dataFinal[$actionValue] = Common::replaceFieldWithValue($value->customValue, $data); + } elseif (!\is_null($data[$triggerValue])) { + $dataFinal[$actionValue] = $data[$triggerValue]; + } + } + + return $dataFinal; + } + + public function execute( + $channels, + $emailStatus, + $smsStatus, + $fieldValues, + $fieldMap, + $customFieldMap + ) { + $finalData = $this->generateReqDataFromFieldMap($fieldValues, $fieldMap); + $customProperties = Hooks::apply(Config::withPrefix('omnisend_custom_properties'), (object) [], $customFieldMap, $fieldValues); + + /** + * @deprecated 2.7.8 Use `bit_integrations_omnisend_custom_properties` filter instead. + * @since 2.7.8 + */ + $customProperties = Hooks::apply('btcbi_omnisend_custom_properties', $customProperties, $customFieldMap, $fieldValues); + + $apiResponse = $this->addContact( + $channels, + $emailStatus, + $smsStatus, + $finalData, + $customProperties + ); + + if (isset($apiResponse->error)) { + LogHandler::save($this->_integrationID, wp_json_encode(['type' => 'contact', 'type_name' => 'add-contact']), 'error', wp_json_encode($apiResponse)); + } else { + LogHandler::save($this->_integrationID, wp_json_encode(['type' => 'contact', 'type_name' => 'add-contact']), 'success', wp_json_encode($apiResponse)); + } + + return $apiResponse; + } +} diff --git a/backend/Actions/OmniSend/Routes.php b/backend/Actions/OmniSend/Routes.php new file mode 100644 index 000000000..9d121d959 --- /dev/null +++ b/backend/Actions/OmniSend/Routes.php @@ -0,0 +1,10 @@ +flow_details->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; diff --git a/backend/Actions/OneDrive/RecordApiHelper.php b/backend/Actions/OneDrive/RecordApiHelper.php new file mode 100644 index 000000000..7af718eed --- /dev/null +++ b/backend/Actions/OneDrive/RecordApiHelper.php @@ -0,0 +1,120 @@ +token = $token; + } + + public function uploadFile($folder, $file, $folderId, $parentId) + { + if (\is_null($parentId)) { + // $parentId = 'root'; + $parentId = $folderId; + } + $ids = explode('!', $folderId); + if ($file === '') { + return false; + } + + $filePath = Common::filePath($file); + $apiEndpoint = 'https://api.onedrive.com/v1.0/drives/' . $ids[0] . '/items/' . $parentId . ':/' . basename($filePath) . ':/content'; + + $headers = [ + 'Authorization' => 'Bearer ' . $this->token, + 'Content-Type' => 'application/octet-stream', + 'Content-Length' => filesize($filePath), + 'Prefer' => 'respond-async', + 'X-HTTP-Method' => 'PUT' + ]; + + return HttpHelper::post( + $apiEndpoint, + file_get_contents($filePath), + $headers + ); + } + + public function handleAllFiles($folderWithFile, $actions, $folderId, $parentId) + { + foreach ($folderWithFile as $folder => $filePath) { + if ($filePath == '') { + continue; + } + if (\is_array($filePath)) { + foreach ($filePath as $singleFilePath) { + if ($singleFilePath == '') { + continue; + } + $response = $this->uploadFile($folder, $singleFilePath, $folderId, $parentId); + $this->storeInState($response); + $this->deleteFile($singleFilePath, $actions); + } + } else { + $response = $this->uploadFile($folder, $filePath, $folderId, $parentId); + $this->storeInState($response); + $this->deleteFile($filePath, $actions); + } + } + } + + public function deleteFile($filePath, $actions) + { + if (isset($actions->delete_from_wp) && $actions->delete_from_wp) { + if (file_exists($filePath)) { + wp_delete_file($filePath); + } + } + } + + public function executeRecordApi($integrationId, $fieldValues, $fieldMap, $actions, $folderId, $parentId) + { + $folderWithFile = []; + $actionsAttachments = explode(',', "{$actions->attachments}"); + if (\is_array($actionsAttachments)) { + foreach ($actionsAttachments as $actionAttachment) { + if (\is_array($fieldValues[$actionAttachment])) { + foreach ($fieldValues[$actionAttachment] as $value) { + // key need correction + $folderWithFile = ["{$actionsAttachments}" => $value]; + } + $this->handleAllFiles($folderWithFile, $actions, $folderId, $parentId); + } else { + $folderWithFile = ["{$actionsAttachments}" => $fieldValues[$actionAttachment]]; + $this->handleAllFiles($folderWithFile, $actions, $folderId, $parentId); + } + } + } + + if (\count($this->successApiResponse) > 0) { + LogHandler::save($integrationId, wp_json_encode(['type' => 'OneDrive', 'type_name' => 'file_upload']), 'success', __('All Files Uploaded.', 'bit-integrations') . wp_json_encode($this->successApiResponse)); + } + if (\count($this->errorApiResponse) > 0) { + LogHandler::save($integrationId, wp_json_encode(['type' => 'OneDrive', 'type_name' => 'file_upload']), 'error', __('Some Files Can\'t Upload.', 'bit-integrations') . wp_json_encode($this->errorApiResponse)); + } + } + + protected function storeInState($response) + { + $response = \is_string($response) ? json_decode($response) : $response; + + if (isset($response->id)) { + $this->successApiResponse[] = $response; + } else { + $this->errorApiResponse[] = $response; + } + } +} diff --git a/backend/Actions/OneDrive/Routes.php b/backend/Actions/OneDrive/Routes.php new file mode 100644 index 000000000..8f6f07829 --- /dev/null +++ b/backend/Actions/OneDrive/Routes.php @@ -0,0 +1,12 @@ +domain; if (empty($fieldMap) || empty($apiKey) || empty($apiSecret) || empty($actionName) || empty($domain)) { + // translators: %s: Placeholder value return new WP_Error('REQ_FIELD_EMPTY', wp_sprintf(__('module, fields are required for %s api', 'bit-integrations'), 'OneHashCRM')); } diff --git a/backend/Actions/OneHashCRM/RecordApiHelper.php b/backend/Actions/OneHashCRM/RecordApiHelper.php new file mode 100644 index 000000000..7a91ee4d4 --- /dev/null +++ b/backend/Actions/OneHashCRM/RecordApiHelper.php @@ -0,0 +1,166 @@ +integrationDetails = $integrationDetails; + $this->integrationId = $integId; + $this->apiUrl = "{$domain}/api/resource"; + $this->defaultHeader = [ + 'Authorization' => "token {$apiKey}:{$apiSecret}", + 'Content-type' => 'application/json', + ]; + } + + public function addCustomer($finalData) + { + if (empty($finalData['customer_name'])) { + return ['success' => false, 'message' => __('Required field Full Name is empty', 'bit-integrations'), 'code' => 400]; + } elseif (!isset($this->integrationDetails->selectedCustomerType) || empty($this->integrationDetails->selectedCustomerType)) { + return ['success' => false, 'message' => __('Required field Customer Type is empty', 'bit-integrations'), 'code' => 400]; + } + + $finalData['customer_type'] = $this->integrationDetails->selectedCustomerType; + $finalData['customer_group'] = 'All Customer Groups'; + $finalData['territory'] = 'All Territories'; + $this->type = 'Customer'; + $this->typeName = 'Customer created'; + $apiEndpoint = $this->apiUrl . '/Customer'; + + return HttpHelper::post($apiEndpoint, wp_json_encode($finalData), $this->defaultHeader); + } + + public function addContact($finalData) + { + if (empty($finalData['first_name'])) { + return ['success' => false, 'message' => __('Required field First Name is empty', 'bit-integrations'), 'code' => 400]; + } + + if (isset($this->integrationDetails->selectedContactStatus) && !empty($this->integrationDetails->selectedContactStatus)) { + $finalData['status'] = ($this->integrationDetails->selectedContactStatus); + } + + if (isset($finalData['email_id'])) { + $finalData['email_ids'] = [ + (object) [ + 'email_id' => $finalData['email_id'], + 'is_primary' => true + ] + ]; + } + if (isset($finalData['phone'])) { + $finalData['phone_nos'][] = (object) [ + 'phone' => $finalData['phone'], + 'is_primary_phone' => true + ]; + } + if (isset($finalData['mobile_no'])) { + $finalData['phone_nos'][] = (object) [ + 'phone' => $finalData['mobile_no'], + 'is_primary_mobile_no' => true + ]; + } + + $this->type = 'Contact'; + $this->typeName = 'Contact created'; + $apiEndpoint = $this->apiUrl . '/Contact'; + + return HttpHelper::post($apiEndpoint, wp_json_encode($finalData), $this->defaultHeader); + } + + public function addLead($finalData) + { + if (empty($finalData['lead_name'])) { + return ['success' => false, 'message' => __('Required Person Name is empty', 'bit-integrations'), 'code' => 400]; + } elseif (empty($finalData['company_name'])) { + return ['success' => false, 'message' => __('Required Organization Name is empty', 'bit-integrations'), 'code' => 400]; + } elseif (!isset($this->integrationDetails->selectedLeadStatus) || empty($this->integrationDetails->selectedLeadStatus)) { + return ['success' => false, 'message' => __('Required Lead Status is empty', 'bit-integrations'), 'code' => 400]; + } + + if (isset($this->integrationDetails->selectedLeadSource) && !empty($this->integrationDetails->selectedLeadSource)) { + $finalData['source'] = ($this->integrationDetails->selectedLeadSource); + } + if (isset($this->integrationDetails->actions->organizationLead) && !empty($this->integrationDetails->actions->organizationLead)) { + $finalData['organization_lead'] = $this->integrationDetails->actions->organizationLead; + } + if (isset($this->integrationDetails->selectedLeadAddressType) && !empty($this->integrationDetails->selectedLeadAddressType)) { + $finalData['address_type'] = $this->integrationDetails->selectedLeadAddressType; + } + if (isset($this->integrationDetails->selectedLeadType) && !empty($this->integrationDetails->selectedLeadType)) { + $finalData['type'] = $this->integrationDetails->selectedLeadType; + } + if (isset($this->integrationDetails->selectedRequestType) && !empty($this->integrationDetails->selectedRequestType)) { + $finalData['request_type'] = $this->integrationDetails->selectedRequestType; + } + if (isset($this->integrationDetails->selectedMarketSegment) && !empty($this->integrationDetails->selectedMarketSegment)) { + $finalData['market_segment'] = $this->integrationDetails->selectedMarketSegment; + } + + $finalData['status'] = $this->integrationDetails->selectedLeadStatus; + $finalData['territory'] = 'All Territories'; + $this->type = 'Lead'; + $this->typeName = 'Lead created'; + $apiEndpoint = $this->apiUrl . '/Lead'; + + return HttpHelper::post($apiEndpoint, wp_json_encode($finalData), $this->defaultHeader); + } + + public function generateReqDataFromFieldMap($data, $fieldMap) + { + $dataFinal = []; + foreach ($fieldMap as $value) { + $triggerValue = $value->formField; + $actionValue = $value->oneHashCRMFormField; + $dataFinal[$actionValue] = ($triggerValue === 'custom') ? $value->customValue : $data[$triggerValue]; + } + + return $dataFinal; + } + + public function execute($fieldValues, $fieldMap, $actionName) + { + $finalData = $this->generateReqDataFromFieldMap($fieldValues, $fieldMap); + if ($actionName === 'customer') { + $apiResponse = $this->addCustomer($finalData); + } elseif ($actionName === 'contact') { + $apiResponse = $this->addContact($finalData); + } elseif ($actionName === 'lead') { + $apiResponse = $this->addLead($finalData); + } + + if (isset($apiResponse->data)) { + $res = [$this->typeName . ' successfully']; + LogHandler::save($this->integrationId, wp_json_encode(['type' => $this->type, 'type_name' => $this->typeName]), 'success', wp_json_encode($res)); + } else { + LogHandler::save($this->integrationId, wp_json_encode(['type' => $this->type, 'type_name' => $this->type . ' creating']), 'error', wp_json_encode($apiResponse)); + } + + return $apiResponse; + } +} diff --git a/backend/Actions/OneHashCRM/Routes.php b/backend/Actions/OneHashCRM/Routes.php new file mode 100644 index 000000000..3cacbe6e3 --- /dev/null +++ b/backend/Actions/OneHashCRM/Routes.php @@ -0,0 +1,10 @@ +flow_details->tokenDetails->access_token)) { + // 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')); return false; diff --git a/backend/Actions/PCloud/RecordApiHelper.php b/backend/Actions/PCloud/RecordApiHelper.php new file mode 100644 index 000000000..d5d760e97 --- /dev/null +++ b/backend/Actions/PCloud/RecordApiHelper.php @@ -0,0 +1,109 @@ +token = $token; + } + + public function uploadFile($folder, $filePath) + { + if ($filePath === '') { + return false; + } + + if (\is_array($filePath)) { + foreach ($filePath as $item) { + $response = HttpHelper::post( + 'https://api.pcloud.com/uploadfile?folderid=' . $folder, + [ + 'filename' => new CURLFile($item) + ], + [ + 'Content-Type' => 'multipart/form-data', + 'Authorization' => 'Bearer ' . $this->token + ] + ); + } + } else { + $response = HttpHelper::post( + 'https://api.pcloud.com/uploadfile?folderid=' . $folder, + [ + 'filename' => new CURLFile($filePath) + ], + [ + 'Content-Type' => 'multipart/form-data', + 'Authorization' => 'Bearer ' . $this->token + ] + ); + } + + return $response; + } + + public function handleAllFiles($folderWithFiles, $actions) + { + foreach ($folderWithFiles as $folderWithFile) { + if ($folderWithFile == '') { + continue; + } + foreach ($folderWithFile as $folder => $singleFilePath) { + if ($singleFilePath == '') { + continue; + } + $response = $this->uploadFile($folder, \is_array($singleFilePath) ? $singleFilePath[0] : $singleFilePath); + $this->storeInState($response); + $this->deleteFile($singleFilePath[0], $actions); + } + } + } + + public function deleteFile($filePath, $actions) + { + if (isset($actions->delete_from_wp) && $actions->delete_from_wp) { + if (file_exists($filePath)) { + wp_delete_file($filePath); + } + } + } + + public function executeRecordApi($integrationId, $fieldValues, $fieldMap, $actions) + { + foreach ($fieldMap as $value) { + if (!\is_null($fieldValues[$value->formField])) { + $folderWithFiles[] = [$value->pCloudFormField => $fieldValues[$value->formField]]; + } + } + + $this->handleAllFiles($folderWithFiles, $actions); + + if (\count($this->successApiResponse) > 0) { + LogHandler::save($integrationId, wp_json_encode(['type' => 'PCloud', 'type_name' => 'file_upload']), 'success', __('All Files Uploaded', 'bit-integrations') . wp_json_encode($this->successApiResponse)); + } + if (\count($this->errorApiResponse) > 0) { + LogHandler::save($integrationId, wp_json_encode(['type' => 'PCloud', 'type_name' => 'file_upload']), 'error', __('Some Files Can\'t Upload', 'bit-integrations') . wp_json_encode($this->errorApiResponse)); + } + } + + protected function storeInState($response) + { + if (isset($response->metadata[0]->id)) { + $this->successApiResponse[] = $response; + } else { + $this->errorApiResponse[] = $response; + } + } +} diff --git a/backend/Actions/PCloud/Routes.php b/backend/Actions/PCloud/Routes.php new file mode 100644 index 000000000..582a40766 --- /dev/null +++ b/backend/Actions/PCloud/Routes.php @@ -0,0 +1,11 @@ +pmpro_membership_levels); + + // phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.PreparedSQL.NotPrepared + $levels = $wpdb->get_results( + 'SELECT * FROM ' . $membership_table . ' ORDER BY id ASC' + ); + // phpcs:enable + + wp_cache_set($cache_key, $levels, $cache_group, 10 * MINUTE_IN_SECONDS); + } + + $allLevels = []; + + if ($levels) { + foreach ($levels as $level) { + $allLevels[] = [ + 'membershipId' => $level->id, + 'membershipTitle' => $level->name, + ]; + } + } + + // $allLevels = array_merge($allLevels, [[ + // 'membershipId' => 'any', + // 'membershipTitle' => 'Any Membership Level', + // ]]); + wp_send_json_success($allLevels); + } + + public function execute($integrationData, $fieldValues) + { + $integrationDetails = $integrationData->flow_details; + $integId = $integrationData->id; + $mainAction = $integrationDetails->mainAction; + $selectedMembership = $integrationDetails->selectedMembership; + if ( + empty($integId) + || empty($mainAction) || empty($selectedMembership) + ) { + return new WP_Error('REQ_FIELD_EMPTY', __('module, There is an some error.', 'bit-integrations')); + } + $recordApiHelper = new RecordApiHelper($integrationDetails, $integId); + $paidMemberpressApiResponse = $recordApiHelper->execute( + $mainAction, + $selectedMembership, + ); + + if (is_wp_error($paidMemberpressApiResponse)) { + return $paidMemberpressApiResponse; + } + + return $paidMemberpressApiResponse; + } +} diff --git a/backend/Actions/PaidMembershipPro/RecordApiHelper.php b/backend/Actions/PaidMembershipPro/RecordApiHelper.php new file mode 100644 index 000000000..f890d2d2d --- /dev/null +++ b/backend/Actions/PaidMembershipPro/RecordApiHelper.php @@ -0,0 +1,153 @@ +_integrationDetails = $integrationDetails; + self::$integrationID = $integId; + } + + public function generateReqDataFromFieldMap($data, $fieldMap) + { + $dataFinal = []; + + foreach ($fieldMap as $key => $value) { + $triggerValue = $value->formField; + $actionValue = $value->memberpressFormField; + if ($triggerValue === 'custom') { + $dataFinal[$actionValue] = Common::replaceFieldWithValue($value->customValue, $data); + } elseif (!\is_null($data[$triggerValue])) { + $dataFinal[$actionValue] = $data[$triggerValue]; + } + } + + return $dataFinal; + } + + public function addUserMembershipLevel($membership_level) + { + $user_id = get_current_user_id(); + $current_level = pmpro_getMembershipLevelForUser($user_id); + + if (!empty($current_level) && absint($current_level->ID) == absint($membership_level)) { + LogHandler::save(self::$integrationID, wp_json_encode(['type' => 'add user', 'type_name' => 'Add the user to a membership level']), 'error', wp_json_encode(__('User is already a member of the specified level.', 'bit-integrations'))); + + return; + } + global $wpdb; + $cache_key = Config::withPrefix('pmpro_membership_level_') . absint($membership_level); + $cache_group = Config::VAR_PREFIX; + $pmpro_membership_level = wp_cache_get($cache_key, $cache_group); + + if (false === $pmpro_membership_level) { + $membership_table = esc_sql($wpdb->pmpro_membership_levels); + // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.PreparedSQL.NotPrepared -- Reading PMPro levels table directly; static table name from PMPro. + $pmpro_membership_level = $wpdb->get_row($wpdb->prepare('SELECT * FROM ' . $membership_table . ' WHERE id = %d', $membership_level)); + wp_cache_set($cache_key, $pmpro_membership_level, $cache_group, 10 * MINUTE_IN_SECONDS); + } + + if (null === $pmpro_membership_level) { + LogHandler::save(self::$integrationID, wp_json_encode(['type' => 'add user', 'type_name' => 'Add the user to a membership level']), 'error', wp_json_encode(__('There is no membership level with the specified ID.', 'bit-integrations'))); + + return; + } + + $isAssigned = null; + if (!empty($pmpro_membership_level->expiration_number)) { + $data = [ + 'user_id' => $user_id, + 'membership_id' => $pmpro_membership_level->id, + 'code_id' => 0, + 'initial_payment' => 0, + 'billing_amount' => 0, + 'cycle_number' => 0, + 'cycle_period' => 0, + 'billing_limit' => 0, + 'trial_amount' => 0, + 'trial_limit' => 0, + ]; + + $isAssigned = pmpro_changeMembershipLevel($data, absint($user_id)); + } else { + $isAssigned = pmpro_changeMembershipLevel(absint($membership_level), absint($user_id)); + } + + if ($isAssigned === true) { + LogHandler::save(self::$integrationID, wp_json_encode(['type' => 'add user', 'type_name' => 'Add the user to a membership level']), 'success', wp_json_encode(__('User membership level added successfully', 'bit-integrations'))); + + return; + } + LogHandler::save(self::$integrationID, wp_json_encode(['type' => 'add user', 'type_name' => 'Add the user to a membership level']), 'error', wp_json_encode(__('Failed to add membership level', 'bit-integrations'))); + } + + public function removeUserFromMembershipLevel($membership_level) + { + $user_id = get_current_user_id(); + $user_membership_levels = $this->get_user_membership_levels($user_id); + + if ('any' === $membership_level) { + if (empty($user_membership_levels)) { + LogHandler::save(self::$integrationID, wp_json_encode(['type' => 'remove user', 'type_name' => 'Remove the user to all membership level']), 'error', wp_json_encode(__('User does not belong to any membership levels', 'bit-integrations'))); + + return; + } + + foreach ($user_membership_levels as $membership_level) { + $cancel_level = pmpro_cancelMembershipLevel(absint($membership_level), absint($user_id)); + LogHandler::save(self::$integrationID, wp_json_encode(['type' => 'remove user', 'type_name' => 'Remove the user to a membership level']), 'success', wp_json_encode(__('User removed from all membership level successfully', 'bit-integrations'))); + } + } + + if (!\in_array($membership_level, $user_membership_levels, true)) { + LogHandler::save(self::$integrationID, wp_json_encode(['type' => 'remove user', 'type_name' => 'Remove the user to all membership level']), 'error', wp_json_encode(__('User was not a member of the specified level', 'bit-integrations'))); + + return; + } + + if (pmpro_cancelMembershipLevel(absint($membership_level), absint($user_id))) { + LogHandler::save(self::$integrationID, wp_json_encode(['type' => 'remove user', 'type_name' => 'Remove the user to a membership level']), 'success', wp_json_encode(__('User removed from membership level successfully', 'bit-integrations'))); + + return; + } + } + + public function execute( + $mainAction, + $selectedMembership + ) { + $apiResponse = true; + if ($mainAction === '1') { + $this->addUserMembershipLevel($selectedMembership); + } elseif ($mainAction === '2') { + $this->removeUserFromMembershipLevel($selectedMembership); + } + + return $apiResponse; + } + + protected function get_user_membership_levels($user_id = 0) + { + if (!\function_exists('pmpro_getMembershipLevelsForUser')) { + return []; + } + $user_membership_levels = pmpro_getMembershipLevelsForUser($user_id); + + return array_map( + function ($membership_level) { + return $membership_level->ID; + }, + $user_membership_levels + ); + } +} diff --git a/backend/Actions/PaidMembershipPro/Routes.php b/backend/Actions/PaidMembershipPro/Routes.php new file mode 100644 index 000000000..3d42c0bab --- /dev/null +++ b/backend/Actions/PaidMembershipPro/Routes.php @@ -0,0 +1,11 @@ +domain; if (empty($fieldMap) || empty($apiToken) || empty($actionName) || empty($domain)) { + // translators: %s: Placeholder value return new WP_Error('REQ_FIELD_EMPTY', wp_sprintf(__('module, fields are required for %s api', 'bit-integrations'), 'PerfexCRM')); } diff --git a/backend/Actions/PerfexCRM/RecordApiHelper.php b/backend/Actions/PerfexCRM/RecordApiHelper.php new file mode 100644 index 000000000..07f4ba87c --- /dev/null +++ b/backend/Actions/PerfexCRM/RecordApiHelper.php @@ -0,0 +1,191 @@ +integrationDetails = $integrationDetails; + $this->integrationId = $integId; + $this->apiUrl = "{$domain}/api"; + $this->defaultHeader = [ + 'authtoken' => $apiToken, + 'Content-type' => 'application/json', + 'Content-type' => 'application/x-www-form-urlencoded', + ]; + } + + public function addCustomer($finalData) + { + if (empty($finalData['company'])) { + return ['success' => false, 'message' => __('Required field Company is empty', 'bit-integrations'), 'code' => 400]; + } + + $this->type = 'Customer'; + $this->typeName = 'Customer created'; + $apiEndpoint = $this->apiUrl . '/customers'; + + return HttpHelper::post($apiEndpoint, $finalData, $this->defaultHeader); + } + + public function addContact($finalData) + { + if (empty($finalData['firstname'])) { + return ['success' => false, 'message' => __('Required field First Name is empty', 'bit-integrations'), 'code' => 400]; + } elseif (empty($finalData['lastname'])) { + return ['success' => false, 'message' => __('Required field Last Name is empty', 'bit-integrations'), 'code' => 400]; + } elseif (empty($finalData['email'])) { + return ['success' => false, 'message' => __('Required field Email is empty', 'bit-integrations'), 'code' => 400]; + } elseif (isset($this->integrationDetails->selectedCustomer) && empty($this->integrationDetails->selectedCustomer)) { + return ['success' => false, 'message' => __('Required field Customer is empty', 'bit-integrations'), 'code' => 400]; + } + + $finalData['customer_id'] = ($this->integrationDetails->selectedCustomer); + $finalData['send_set_password_email'] = 'on'; + + if (isset($this->integrationDetails->selectedDirection) && !empty($this->integrationDetails->selectedDirection)) { + $finalData['direction'] = ($this->integrationDetails->selectedDirection); + } + if (isset($this->integrationDetails->selectedPermission) && !empty($this->integrationDetails->selectedPermission)) { + $finalData['permissions'] = explode(',', $this->integrationDetails->selectedPermission); + } + if (isset($this->integrationDetails->actions->contactIsPrimary) && !empty($this->integrationDetails->actions->contactIsPrimary)) { + $finalData['is_primary'] = $this->integrationDetails->actions->contactIsPrimary ? 'on' : $this->integrationDetails->actions->contactIsPrimary; + } + + $this->type = 'Contact'; + $this->typeName = 'Contact created'; + $apiEndpoint = $this->apiUrl . '/contacts'; + + return HttpHelper::post($apiEndpoint, $finalData, $this->defaultHeader); + } + + public function addLead($finalData) + { + if (empty($finalData['name'])) { + return ['success' => false, 'message' => __('Required field name is empty', 'bit-integrations'), 'code' => 400]; + } elseif (isset($this->integrationDetails->selectedLeadStatusId) && empty($this->integrationDetails->selectedLeadStatusId)) { + return ['success' => false, 'message' => __('Required field Lead Status Id is empty', 'bit-integrations'), 'code' => 400]; + } elseif (isset($this->integrationDetails->selectedLeadSourceId) && empty($this->integrationDetails->selectedLeadSourceId)) { + return ['success' => false, 'message' => __('Required field Lead Source Id is empty', 'bit-integrations'), 'code' => 400]; + } elseif (isset($this->integrationDetails->selectedStaff) && empty($this->integrationDetails->selectedStaff)) { + return ['success' => false, 'message' => __('Required field Lead Assigned By is empty', 'bit-integrations'), 'code' => 400]; + } + + $finalData['status'] = $this->integrationDetails->selectedLeadStatusId; + $finalData['source'] = $this->integrationDetails->selectedLeadSourceId; + $finalData['assigned'] = $this->integrationDetails->selectedStaff; + + if (isset($this->integrationDetails->selectedCustomer) && !empty($this->integrationDetails->selectedCustomer)) { + $finalData['client_id'] = ($this->integrationDetails->selectedCustomer); + } + if (isset($this->integrationDetails->actions->leadIsPublic) && !empty($this->integrationDetails->actions->leadIsPublic)) { + $finalData['is_public'] = $this->integrationDetails->actions->leadIsPublic; + } + if (isset($this->integrationDetails->actions->contactedToday) && !empty($this->integrationDetails->actions->contactedToday)) { + $finalData['contacted_today'] = $this->integrationDetails->actions->contactedToday; + } + $this->type = 'Lead'; + $this->typeName = 'Lead created'; + $apiEndpoint = $this->apiUrl . '/leads'; + + return HttpHelper::post($apiEndpoint, $finalData, $this->defaultHeader); + } + + public function addProject($finalData) + { + if (empty($finalData['name'])) { + return ['success' => false, 'message' => __('Required field name is empty', 'bit-integrations'), 'code' => 400]; + } elseif (empty($finalData['start_date'])) { + return ['success' => false, 'message' => __('Required field Start Date is empty', 'bit-integrations'), 'code' => 400]; + } elseif (isset($this->integrationDetails->selectedProjectStatus) && empty($this->integrationDetails->selectedProjectStatus)) { + return ['success' => false, 'message' => __('Required field Project Status is empty', 'bit-integrations'), 'code' => 400]; + } elseif (isset($this->integrationDetails->selectedProjectType) && empty($this->integrationDetails->selectedProjectType)) { + return ['success' => false, 'message' => __('Required field Project Type is empty', 'bit-integrations'), 'code' => 400]; + } elseif (isset($this->integrationDetails->selectedbillingType) && empty($this->integrationDetails->selectedbillingType)) { + return ['success' => false, 'message' => __('Required field Billing Type is empty', 'bit-integrations'), 'code' => 400]; + } elseif (isset($this->integrationDetails->selectedCustomer) && empty($this->integrationDetails->selectedCustomer)) { + return ['success' => false, 'message' => __('Required field Customer is empty', 'bit-integrations'), 'code' => 400]; + } + + $finalData['status'] = $this->integrationDetails->selectedProjectStatus; + $finalData['rel_type'] = $this->integrationDetails->selectedProjectType; + $finalData['billing_type'] = $this->integrationDetails->selectedbillingType; + $finalData['clientid'] = $this->integrationDetails->selectedCustomer; + + if ($this->integrationDetails->selectedbillingType === 1) { + $finalData['project_cost'] = $this->integrationDetails->totalRate; + } elseif ($this->integrationDetails->selectedbillingType === 2) { + $finalData['project_rate_per_hour'] = $this->integrationDetails->ratePerHour; + } + + if (isset($this->integrationDetails->selectedProjectMembers) && !empty($this->integrationDetails->selectedProjectMembers)) { + $finalData['project_members'] = explode(',', $this->integrationDetails->selectedProjectMembers); + } + + $this->type = 'Project'; + $this->typeName = 'Project created'; + $apiEndpoint = $this->apiUrl . '/projects'; + + return HttpHelper::post($apiEndpoint, $finalData, $this->defaultHeader); + } + + public function generateReqDataFromFieldMap($data, $fieldMap) + { + $dataFinal = []; + foreach ($fieldMap as $value) { + $triggerValue = $value->formField; + $actionValue = $value->perfexCRMFormField; + $dataFinal[$actionValue] = ($triggerValue === 'custom' && !empty($value->customValue)) ? Common::replaceFieldWithValue($value->customValue, $data) : $data[$triggerValue]; + } + + return $dataFinal; + } + + public function execute($fieldValues, $fieldMap, $actionName) + { + $finalData = $this->generateReqDataFromFieldMap($fieldValues, $fieldMap); + if ($actionName === 'customer') { + $apiResponse = $this->addCustomer($finalData); + } elseif ($actionName === 'contact') { + $apiResponse = $this->addContact($finalData); + } elseif ($actionName === 'lead') { + $apiResponse = $this->addLead($finalData); + } elseif ($actionName === 'project') { + $apiResponse = $this->addProject($finalData); + } + + if (!empty($apiResponse->status)) { + $res = [$this->typeName . ' successfully']; + LogHandler::save($this->integrationId, wp_json_encode(['type' => $this->type, 'type_name' => $this->typeName]), 'success', wp_json_encode($res)); + } else { + LogHandler::save($this->integrationId, wp_json_encode(['type' => $this->type, 'type_name' => $this->type . ' creating']), 'error', wp_json_encode($apiResponse)); + } + + return $apiResponse; + } +} diff --git a/backend/Actions/PerfexCRM/Routes.php b/backend/Actions/PerfexCRM/Routes.php new file mode 100644 index 000000000..b5c2ffbb9 --- /dev/null +++ b/backend/Actions/PerfexCRM/Routes.php @@ -0,0 +1,14 @@ +success, $pipeDriveApiResponse->data) && $pipeDriveApiResponse->success && \count($integrationDetails->relatedlists)) { - do_action('btcbi_pipedrive_store_related_list', $pipeDriveApiResponse, $integrationDetails, $fieldValues, $module, $integrationDetails->api_key, $integId); + Hooks::run(Config::withPrefix('pipedrive_store_related_list'), $pipeDriveApiResponse, $integrationDetails, $fieldValues, $module, $integrationDetails->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); } return $pipeDriveApiResponse; diff --git a/backend/Actions/PipeDrive/RecordApiHelper.php b/backend/Actions/PipeDrive/RecordApiHelper.php new file mode 100644 index 000000000..5b542c73c --- /dev/null +++ b/backend/Actions/PipeDrive/RecordApiHelper.php @@ -0,0 +1,172 @@ +_integrationDetails = $integrationDetails; + $this->_integrationID = $integId; + $this->_defaultHeader = [ + 'content-type' => 'application/json' + ]; + } + + public function insertRecord( + $module, + $finalData + ) { + $moduleData = $this->_integrationDetails->moduleData; + $apiEndpoints = $this->baseUrl . $module . '?api_token=' . $this->_integrationDetails->api_key; + $actions = $this->_integrationDetails->actions; + + if ($module !== 'products') { + if (isset($moduleData->organization_id)) { + if ($module !== 'leads') { + $finalData['org_id'] = $moduleData->organization_id; + } else { + $finalData['organization_id'] = $moduleData->organization_id; + } + } + if (isset($moduleData->person_id)) { + $finalData['person_id'] = $moduleData->person_id; + } + if ($module === 'leads' && isset($actions->currency) && !empty($actions->currency)) { + $finalData['value'] = (object) [ + 'amount' => isset($finalData['value']) ? (int) $finalData['value'] : 0, + 'currency' => $moduleData->currency, + ]; + unset($finalData['currency']); + } + if ($module === 'leads' && !isset($actions->currency) && isset($finalData['value'])) { + $finalData['value'] = (object) [ + 'amount' => (int) $finalData['value'] + ]; + unset($finalData['value']); + } + } + if (isset($moduleData->owner) && !empty($moduleData->owner)) { + if (\in_array($module, ['activites', 'notes', 'deals'])) { + $finalData['user_id'] = (int) $moduleData->owner; + } else { + $finalData['owner_id'] = (int) $moduleData->owner; + } + } + if (isset($moduleData->lead_label) && !empty($moduleData->lead_label)) { + $finalData['label_ids'] = explode(',', $moduleData->lead_label); + } + if (isset($moduleData->deal_stage) && !empty($moduleData->deal_stage)) { + $finalData['stage_id'] = (int) $moduleData->deal_stage; + } + if (isset($moduleData->activities_type) && !empty($moduleData->activities_type)) { + $finalData['type'] = $moduleData->activities_type; + } + if (isset($actions->busy_flag) && !empty($actions->busy_flag)) { + $finalData['busy_flag'] = true; + } + if (isset($actions->active_flag) && !empty($actions->active_flag)) { + $finalData['active_flag'] = 0; + } + + if (isset($actions->currency) && !empty($actions->currency)) { + if ($module === 'deals') { + $finalData['currency'] = $moduleData->currency; + } + + if ($module === 'products') { + $finalData['prices'] = [(object) [ + 'currency' => $moduleData->currency, + 'price' => isset($finalData['price']) ? (int) $finalData['price'] : 0, + 'cost' => isset($finalData['cost']) ? (int) $finalData['cost'] : 0, + 'overhead_cost' => isset($finalData['overhead_cost']) ? (int) $finalData['overhead_cost'] : 0, + ]]; + unset($finalData['price'], $finalData['cost'],$finalData['overhead_cost']); + } + } + if ($module === 'products' && !isset($actions->currency)) { + $finalData['prices'] = [(object) [ + 'price' => isset($finalData['price']) ? (int) $finalData['price'] : 0, + 'cost' => isset($finalData['cost']) ? (int) $finalData['cost'] : 0, + 'overhead_cost' => isset($finalData['overhead_cost']) ? (int) $finalData['overhead_cost'] : 0, + ]]; + unset($finalData['price'], $finalData['cost'],$finalData['overhead_cost']); + } + if (isset($actions->deal_status) && !empty($actions->deal_status)) { + $finalData['status'] = $moduleData->deal_status; + } + + if (isset($actions->visible_to) && !empty($actions->visible_to)) { + $finalData['visible_to'] = $moduleData->visible_to; + } + + if (isset($actions->activities_participants) && !empty($actions->activities_participants)) { + $participants = explode(',', $moduleData->activities_participants); + $allParticipants = []; + foreach ($participants as $participant) { + $allParticipants[] = (object) [ + 'person_id' => (int) $participant, + 'primary_flag' => false + ]; + } + $finalData['participants'] = $allParticipants; + } + + return HttpHelper::post($apiEndpoints, wp_json_encode($finalData), $this->_defaultHeader); + } + + public function generateReqDataFromFieldMap($data, $fieldMap) + { + $dataFinal = []; + foreach ($fieldMap as $key => $value) { + $triggerValue = $value->formField; + $actionValue = $value->pipeDriveFormField; + if ($triggerValue === 'custom') { + $dataFinal[$actionValue] = Common::replaceFieldWithValue($value->customValue, $data); + } elseif (!\is_null($data[$triggerValue])) { + $dataFinal[$actionValue] = \is_array($data[$triggerValue]) ? implode(',', $data[$triggerValue]) : $data[$triggerValue]; + } + } + + return $dataFinal; + } + + public function execute( + $fieldValues, + $fieldMap, + $module + ) { + $finalData = $this->generateReqDataFromFieldMap($fieldValues, $fieldMap); + $apiResponse = $this->insertRecord( + $module, + $finalData + ); + + if (isset($apiResponse->error)) { + LogHandler::save($this->_integrationID, wp_json_encode(['type' => $module, 'type_name' => 'add-' . $module]), 'error', wp_json_encode($apiResponse)); + } else { + LogHandler::save($this->_integrationID, wp_json_encode(['type' => $module, 'type_name' => 'add-' . $module]), 'success', wp_json_encode($apiResponse)); + } + + return $apiResponse; + } +} diff --git a/backend/Actions/PipeDrive/Routes.php b/backend/Actions/PipeDrive/Routes.php new file mode 100644 index 000000000..e397fdae9 --- /dev/null +++ b/backend/Actions/PipeDrive/Routes.php @@ -0,0 +1,11 @@ +post_tags ?? null); + Hooks::run(Config::withPrefix('add_post_tag'), $postId, $flowDetails->post_tags ?? null); + + /** + * @deprecated 2.7.8 Use `bit_integrations_add_post_tag` action instead. + * @since 2.7.8 + */ + Hooks::run('btcbi_add_post_tag', $postId, $flowDetails->post_tags ?? null); $result = wp_update_post($updateData, true); diff --git a/backend/Actions/PropovoiceCRM/FilesApiHelper.php b/backend/Actions/PropovoiceCRM/FilesApiHelper.php new file mode 100644 index 000000000..bb6fdb1fa --- /dev/null +++ b/backend/Actions/PropovoiceCRM/FilesApiHelper.php @@ -0,0 +1,116 @@ +add('field', wp_sprintf(__('Invalid file type: %1$s. Supported file types: %2$s', 'bit-integrations'), $error_file_type, $valid_file_type)); + } + + if (!empty($reg_errors->get_error_messages())) { + wp_send_json_error($reg_errors->get_error_messages()); + } else { + if (!\function_exists('wp_handle_upload')) { + require_once ABSPATH . 'wp-admin/includes/file.php'; + } + $upload_overrides = ['test_form' => false, 'test_upload' => false]; + $uploaded = wp_handle_sideload($file, $upload_overrides); + + if ($uploaded && !isset($uploaded['error'])) { + $filename = $uploaded['file']; + $filetype = wp_check_filetype(basename($filename), null); + + $attach_id = wp_insert_attachment( + [ + 'guid' => $uploaded['url'], + 'post_title' => sanitize_text_field( + preg_replace( + '/\.[^.]+$/', + '', + basename($filename) + ) + ), + 'post_excerpt' => '', + 'post_content' => '', + 'post_mime_type' => sanitize_text_field( + $filetype['type'] + ), + 'comments_status' => 'closed', + ], + $uploaded['file'], + 0 + ); + + $file_info = []; + if (!is_wp_error($attach_id)) { + // wp_update_attachment_metadata($attach_id, wp_generate_attachment_metadata($attach_id, $filename)); + update_post_meta( + $attach_id, + 'ws_id', + ndpv()->get_workspace() + ); + update_post_meta( + $attach_id, + 'ndpv_attach_type', + $attach_type + ); + + $file_info = [ + 'id' => $attach_id, + 'type' => get_post_mime_type($attach_id), + 'name' => basename(get_attached_file($attach_id)), + 'src' => wp_get_attachment_image_url( + $attach_id, + 'thumbnail' + ), + ]; + + if ($file_info['type'] == 'application/pdf') { + $file_info['name'] = basename(get_attached_file($attach_id)); + $file_info['src'] = wp_get_attachment_url($attach_id); + } + } + + return $file_info; + } + wp_send_json_error($uploaded['error']); + } + } + } + + private static function simulateFileUpload($file_path) + { + if (!file_exists($file_path)) { + return false; + } + + return [ + 'name' => basename($file_path), + 'type' => mime_content_type($file_path), + 'tmp_name' => $file_path, + 'error' => 0, + 'size' => filesize($file_path), + ]; + } +} diff --git a/backend/Actions/PropovoiceCRM/PropovoiceCRMController.php b/backend/Actions/PropovoiceCRM/PropovoiceCRMController.php new file mode 100644 index 000000000..ecbc9807e --- /dev/null +++ b/backend/Actions/PropovoiceCRM/PropovoiceCRMController.php @@ -0,0 +1,80 @@ +get_results($wpdb->prepare( + "SELECT term_id, name FROM {$wpdb->terms} WHERE term_id IN (SELECT term_taxonomy_id FROM {$wpdb->term_taxonomy} WHERE taxonomy = %s)", + 'ndpv_tag' + )); + wp_send_json_success($tags, 200); + } + + public static function leadLabel() + { + global $wpdb; + // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Direct query needed for Propovoice labels + $labels = $wpdb->get_results($wpdb->prepare( + "SELECT term_id, name FROM {$wpdb->terms} WHERE term_id IN (SELECT term_taxonomy_id FROM {$wpdb->term_taxonomy} WHERE taxonomy = %s)", + 'ndpv_lead_level' + )); + wp_send_json_success($labels, 200); + } + + public function execute($integrationData, $fieldValues) + { + $integrationDetails = $integrationData->flow_details; + $integrationId = $integrationData->id; + $fieldMap = $integrationDetails->field_map; + $mainAction = $integrationDetails->mainAction; + + if ( + empty($integrationDetails) + || empty($mainAction) + || empty($fieldMap) + + ) { + // translators: %s: Integration name + + // translators: %s: Placeholder value + return new WP_Error('REQ_FIELD_EMPTY', wp_sprintf(__('module, fields are required for %s api', 'bit-integrations'), 'Propovoice CRM')); + } + $recordApiHelper = new RecordApiHelper($integrationId); + $propovoiceCrmApiResponse = $recordApiHelper->execute( + $fieldValues, + $fieldMap, + $integrationDetails, + $mainAction + ); + + if (is_wp_error($propovoiceCrmApiResponse)) { + return $propovoiceCrmApiResponse; + } + + return $propovoiceCrmApiResponse; + } +} diff --git a/backend/Actions/PropovoiceCRM/RecordApiHelper.php b/backend/Actions/PropovoiceCRM/RecordApiHelper.php new file mode 100644 index 000000000..a74d13275 --- /dev/null +++ b/backend/Actions/PropovoiceCRM/RecordApiHelper.php @@ -0,0 +1,74 @@ +_integrationID = $integrationId; + } + + public function generateReqDataFromFieldMap($data, $fieldMap) + { + $dataFinal = []; + + foreach ($fieldMap as $key => $value) { + $triggerValue = $value->formField; + $actionValue = $value->propovoiceCrmFormField; + if ($triggerValue === 'custom') { + $dataFinal[$actionValue] = Common::replaceFieldWithValue($value->customValue, $data); + } elseif (!\is_null($data[$triggerValue])) { + $dataFinal[$actionValue] = $data[$triggerValue]; + } + } + + return $dataFinal; + } + + public function createLead($finalData) + { + if ($finalData['img']) { + $imgUpload = new FilesApiHelper(); + $upload = $imgUpload->uploadFile($finalData['img'][0]); + $finalData['img'] = $upload['id']; + } + + $propovoiceLeadInstance = new \Ndpv\Model\Lead(); + + return $propovoiceLeadInstance->create($finalData); + } + + public function execute( + $fieldValues, + $fieldMap, + $integrationDetails, + $mainAction + ) { + $finalData = $this->generateReqDataFromFieldMap($fieldValues, $fieldMap); + $apiResponse = null; + if ($mainAction == '1') { + // $tags = is_array($integrationDetails->tags) ? $integrationDetails->tags : explode(',', $integrationDetails->tags); + // $label = $integrationDetails->label; + // $finalData['tags'] = $tags; + // $finalData['level_id'] = $label; + $apiResponse = $this->createLead($finalData); + + if (!$apiResponse) { + LogHandler::save($this->_integrationID, 'Lead', 'success', __('Lead Created Successfully', 'bit-integrations')); + } else { + LogHandler::save($this->_integrationID, 'Lead', 'error', wp_json_encode($apiResponse)); + } + } + + return $apiResponse; + } +} diff --git a/backend/Actions/PropovoiceCRM/Routes.php b/backend/Actions/PropovoiceCRM/Routes.php new file mode 100644 index 000000000..1fa0e61eb --- /dev/null +++ b/backend/Actions/PropovoiceCRM/Routes.php @@ -0,0 +1,12 @@ +integrationDetails = $integrationDetails; + $this->_defaultHeader = [ + 'Authorization' => 'Basic ' . base64_encode("{$username}:{$password}"), + 'Accept' => '*/*', + 'Content-Type' => 'application/json', + 'verify' => false + ]; + } + + public function insertRecipientRecord($data, $actions) + { + $send_activationmail = isset($actions->send_activationmail) && $actions->send_activationmail ? 'yes' : 'no'; + $insertRecordEndpoint = self::$apiBaseUri . "/recipients?send_activationmail={$send_activationmail}"; + + if (isset($actions->force_subscribe) && $actions->force_subscribe) { + $data['status'] = 'active'; + } + + $data = \is_string($data) ? $data : wp_json_encode((object) $data); + + return HttpHelper::post($insertRecordEndpoint, $data, $this->_defaultHeader); + } + + public function generateReqDataFromFieldMap($data, $fieldMap) + { + $dataFinal = []; + + foreach ($fieldMap as $key => $value) { + $triggerValue = $value->formField; + $actionValue = $value->rapidmailFormField; + if ($triggerValue === 'custom') { + $dataFinal[$actionValue] = Common::replaceFieldWithValue($value->customValue, $data); + } elseif (!\is_null($data[$triggerValue])) { + $dataFinal[$actionValue] = $data[$triggerValue]; + } + } + $selectedRecipientList = $this->integrationDetails->recipient_id; + $dataFinal['recipientlist_id'] = (int) $selectedRecipientList; + + return $dataFinal; + } + + public function executeRecordApi($integId, $defaultConf, $recipientLists, $fieldValues, $fieldMap, $actions, $isRelated = false) + { + $finalData = $this->generateReqDataFromFieldMap($fieldValues, $fieldMap); + $apiResponse = $this->insertRecipientRecord($finalData, $actions); + + if (!isset($apiResponse->id)) { + LogHandler::save($integId, wp_json_encode(['type' => 'recipient', 'type_name' => 'recipient_add']), 'error', wp_json_encode($apiResponse)); + } else { + LogHandler::save($integId, wp_json_encode(['type' => 'recipient', 'type_name' => 'recipient_add']), 'success', wp_json_encode($apiResponse)); + } + + return $apiResponse; + } + + public function formatFieldValue($value, $formatSpecs) + { + if (empty($value)) { + return $value; + } + + switch ($formatSpecs->json_type) { + case 'jsonarray': + $apiFormat = 'array'; + + break; + case 'jsonobject': + $apiFormat = 'object'; + + break; + + default: + $apiFormat = $formatSpecs->json_type; + + break; + } + $formatedValue = ''; + $fieldFormat = \gettype($value); + if ($fieldFormat === $apiFormat && $formatSpecs->data_type !== 'datetime') { + $formatedValue = $fieldFormat === 'string' ? html_entity_decode($value) : $value; + } else { + if ($apiFormat === 'array' || $apiFormat === 'object') { + if ($fieldFormat === 'string') { + if (strpos($value, ',') === -1) { + $formatedValue = json_decode($value); + } else { + $formatedValue = explode(',', $value); + } + $formatedValue = \is_null($formatedValue) && !\is_null($value) ? [$value] : $formatedValue; + } else { + $formatedValue = $value; + } + + if ($apiFormat === 'object') { + $formatedValue = (object) $formatedValue; + } + } elseif ($apiFormat === 'string' && $formatSpecs->data_type !== 'datetime' && $formatSpecs->data_type !== 'date') { + $formatedValue = !\is_string($value) ? wp_json_encode($value) : html_entity_decode($value); + } elseif ($formatSpecs->data_type === 'datetime') { + if (\is_array($value)) { + if (isset($value['date'])) { + $value = $value['date']; + $date_format = 'm/d/Y'; + } elseif (isset($value['time'])) { + $value = $value['time']; + $date_format = 'H:i A'; + } elseif (isset($value['time'], $value['date'])) { + $value = isset($value['date']) . ' ' . $value['time']; + $date_format = 'm/d/Y H:i A'; + } else { + $value = '0000-00-00T00:00'; + $date_format = 'Y-m-d\TH:i'; + } + } else { + $date_format = 'Y-m-d\TH:i'; + } + $dateTimeHelper = new DateTimeHelper(); + $formatedValue = $dateTimeHelper->getFormated($value, $date_format, wp_timezone(), 'Y-m-d\TH:i:sP', null); + $formatedValue = !$formatedValue ? null : $formatedValue; + } elseif ($formatSpecs->data_type === 'date') { + if (\is_array($value)) { + if (isset($value['date'])) { + $value = $value['date']; + $date_format = 'm/d/Y'; + } elseif (isset($value['time'])) { + $value = $value['time']; + $date_format = 'H:i A'; + } elseif (isset($value['time'], $value['date'])) { + $value = isset($value['date']) . ' ' . $value['time']; + $date_format = 'm/d/Y H:i A'; + } else { + $value = '0000-00-00T00:00'; + $date_format = 'Y-m-d\TH:i'; + } + } else { + $date_format = 'Y-m-d\TH:i'; + } + $dateTimeHelper = new DateTimeHelper(); + $formatedValue = $dateTimeHelper->getFormated($value, $date_format, wp_timezone(), 'Y-m-d', null); + $formatedValue = !$formatedValue ? null : $formatedValue; + } else { + $stringyfieldValue = !\is_string($value) ? wp_json_encode($value) : $value; + + switch ($apiFormat) { + case 'double': + $formatedValue = (float) $stringyfieldValue; + + break; + + case 'boolean': + $formatedValue = (bool) $stringyfieldValue; + + break; + + case 'integer': + $formatedValue = (int) $stringyfieldValue; + + break; + default: + $formatedValue = $stringyfieldValue; + + break; + } + } + } + if ($apiFormat === 'array' || $apiFormat === 'object') { + $formatedValueLenght = is_countable($formatedValue) ? \count($formatedValue) : \count(get_object_vars($formatedValue)); + } else { + $formatedValueLenght = \strlen($formatedValue); + } + if ($formatedValueLenght > $formatSpecs->length) { + $formatedValue = $apiFormat === 'array' || $apiFormat === 'object' ? \array_slice($formatedValue, 0, $formatSpecs->length) : substr($formatedValue, 0, $formatSpecs->length); + } + + return $formatedValue; + } +} diff --git a/backend/Actions/Rapidmail/Routes.php b/backend/Actions/Rapidmail/Routes.php new file mode 100644 index 000000000..fdf011292 --- /dev/null +++ b/backend/Actions/Rapidmail/Routes.php @@ -0,0 +1,13 @@ +_integrationID, __('New user registration', 'bit-integrations'), 'error', $message); } else { + // translators: %s: Placeholder value LogHandler::save($this->_integrationID, __('New user registration', 'bit-integrations'), 'success', wp_sprintf(__('New user created successfully, user id : %s', 'bit-integrations'), $userId)); $this->saveMetaData($flowDetails->meta_map, $fieldValues, $userId); @@ -167,6 +168,7 @@ private function updateUser($updatedData, $flowDetails, $fieldValues) $message = is_wp_error($updatedUser) ? $updatedUser->get_error_message() : 'error'; LogHandler::save($this->_integrationID, __('User update', 'bit-integrations'), 'error', $message); } else { + // translators: %s: Placeholder value LogHandler::save($this->_integrationID, __('User update', 'bit-integrations'), 'success', wp_sprintf(__('User updated successfully, user id : %s', 'bit-integrations'), $updatedUser)); $this->saveMetaData($flowDetails->meta_map, $fieldValues, $updatedUser); $this->notification($flowDetails, $updatedUser); diff --git a/backend/Actions/RestrictContent/RecordApiHelper.php b/backend/Actions/RestrictContent/RecordApiHelper.php new file mode 100644 index 000000000..211567203 --- /dev/null +++ b/backend/Actions/RestrictContent/RecordApiHelper.php @@ -0,0 +1,188 @@ +_integrationID = $integId; + $this->action = $actionName; + $this->integrationDetails = $integrationDetails; + } + + public function generateReqDataFromFieldMap($data, $fieldMap) + { + $dataFinal = []; + + foreach ($fieldMap as $fieldPair) { + if (!empty($fieldPair->restrictField) && !empty($fieldPair->formField)) { + if ($fieldPair->formField === 'custom' && isset($fieldPair->customValue)) { + $dataFinal[$fieldPair->restrictField] = $fieldPair->customValue; + } else { + $dataFinal[$fieldPair->restrictField] = $data[$fieldPair->formField]; + } + } + } + + return $dataFinal; + } + + public function insertMember($data) + { + $levelId = $this->integrationDetails->level_id; + $actionName = $this->action; + $expiry_date = $this->integrationDetails->exp_date; + + $level_ids = rcp_get_membership_levels( + [ + 'status' => 'active', + 'fields' => 'id', + ] + ); + if (empty($level_ids)) { + $error = new WP_Error('REQ_FIELD_EMPTY', __('You must have at least one active membership level.', 'bit-integrations')); + LogHandler::save($this->_integrationID, 'record', 'validation', $error); + + return $error; + } + + $user_id = get_current_user_id(); + $customer = rcp_get_customer_by_user_id($user_id); + $newest_time = current_time('timestamp'); + $created_date = gmdate('Y-m-d H:i:s', $newest_time); + + if (empty($customer)) { + $customer_id = rcp_add_customer( + [ + 'user_id' => absint($user_id), + 'date_registered' => $created_date, + ] + ); + } else { + $customer_id = $customer->get_id(); + } + + $status = 'active'; + $membership_args = [ + 'customer_id' => absint($customer_id), + 'user_id' => $user_id, + 'object_id' => !empty($levelId) ? $levelId : $level_ids[array_rand($level_ids)], + 'status' => $status, + 'created_date' => $created_date, + 'gateway' => 'manual', + 'subscription_key' => rcp_generate_subscription_key(), + ]; + if (!empty($expiry_date)) { + $membership_args['expiration_date'] = gmdate('Y-m-d H:i:s', strtotime($expiry_date)); + } + $membership_id = rcp_add_membership($membership_args); + + rcp_add_membership_meta($membership_id, 'rcp_generated_via_UA', $this->_integrationID); + + $membership = rcp_get_membership($membership_id); + + $auth_key = \defined('AUTH_KEY') ? AUTH_KEY : ''; + $transaction_id = strtolower(md5($membership_args['subscription_key'] . gmdate('Y-m-d H:i:s') . $auth_key . uniqid('rcp', true))); + + $payment_args = [ + 'subscription' => rcp_get_subscription_name($membership_args['object_id']), + 'object_id' => $membership_args['object_id'], + 'date' => $membership_args['created_date'], + 'amount' => $membership->get_initial_amount(), + 'subtotal' => $membership->get_initial_amount(), + 'user_id' => $user_id, + 'subscription_key' => $membership_args['subscription_key'], + 'transaction_id' => $transaction_id, + 'status' => 'pending' == $membership_args['status'] ? 'pending' : 'complete', + 'gateway' => 'manual', + 'customer_id' => $customer_id, + 'membership_id' => $membership_id, + ]; + + $rcp_payments = new RCP_Payments(); + $payment_id = $rcp_payments->insert($payment_args); + + $rcp_payments->add_meta($payment_id, 'rcp_generated_via_UA', $this->_integrationID); + if ($membership_id) { + return $membership_id; + } + + return false; + } + + public function removeMember() + { + $levelId = $this->integrationDetails->level_id; + $actionName = $this->action; + $user_id = get_current_user_id(); + $ans = []; + if ($levelId == 'all') { + $customer = rcp_get_customer_by_user_id($user_id); + if ($customer->get_id()) { + rcp_disable_customer_memberships($customer->get_id()); + } else { + return $ans['error'] = 'No customer found'; + } + } else { + $customer = rcp_get_customer_by_user_id($user_id); + if ($customer->get_id()) { + $args = [ + 'customer_id' => absint($customer->get_id()), + 'number' => 1, + 'orderby' => 'id', + 'order' => 'ASC', + 'object_id' => $levelId, + ]; + $user_memberships = rcp_get_memberships($args); + if (!empty($user_memberships)) { + $user_memberships[0]->disable(); + } + } else { + return $ans['error'] = 'No customer found'; + } + } + $ans['success'] = 'Membership removed'; + + return $ans; + } + + public function execute($fieldValues, $fieldMap, $integrationDetails) + { + $finalData = $this->generateReqDataFromFieldMap($fieldValues, $fieldMap); + if ($this->action == 'add-member-level') { + $apiResponse = $this->insertMember($finalData); + $type = 'add-member-level'; + $type_name = 'Add member to a Level'; + } elseif ($this->action == 'remove-member-level') { + $apiResponse = $this->removeMember(); + $type = 'remove-member-level'; + $type_name = 'Remove member from a Level'; + } + + if (!$apiResponse) { + LogHandler::save($this->_integrationID, wp_json_encode(['type' => $type, 'type_name' => $type_name]), 'error', wp_json_encode($apiResponse)); + } else { + LogHandler::save($this->_integrationID, wp_json_encode(['type' => $type, 'type_name' => $type_name]), 'success', wp_json_encode($apiResponse)); + } + + return $apiResponse; + } +} diff --git a/includes/Actions/RestrictContent/RestrictContentController.php b/backend/Actions/RestrictContent/RestrictContentController.php similarity index 93% rename from includes/Actions/RestrictContent/RestrictContentController.php rename to backend/Actions/RestrictContent/RestrictContentController.php index b4006a8d8..234b9fcdb 100644 --- a/includes/Actions/RestrictContent/RestrictContentController.php +++ b/backend/Actions/RestrictContent/RestrictContentController.php @@ -4,9 +4,9 @@ * Restrict Content Integration */ -namespace BitCode\FI\Actions\RestrictContent; +namespace BitApps\Integrations\Actions\RestrictContent; -use BitCode\FI\Log\LogHandler; +use BitApps\Integrations\Log\LogHandler; use WP_Error; /** @@ -39,6 +39,8 @@ public static function authorizeRestrictContent() 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')); } diff --git a/backend/Actions/RestrictContent/Routes.php b/backend/Actions/RestrictContent/Routes.php new file mode 100644 index 000000000..aea4a11bb --- /dev/null +++ b/backend/Actions/RestrictContent/Routes.php @@ -0,0 +1,11 @@ +integrationDetails = $integrationDetails; + $this->integrationId = $integId; + $this->apiUrl = 'https://api.salesflare.com'; + $this->defaultHeader = [ + 'Authorization' => "Bearer {$apiKey}", + 'Content-type' => 'application/json', + ]; + } + + public function addAccount($finalData) + { + if (empty($finalData['name'])) { + return ['success' => false, 'message' => __('Required field Account Name is empty', 'bit-integrations'), 'code' => 400]; + } + + $this->type = 'Account'; + $this->typeName = 'Account created'; + $formData = $this->setData($finalData); + $apiEndpoint = $this->apiUrl . '/accounts'; + + return HttpHelper::post($apiEndpoint, wp_json_encode($formData), $this->defaultHeader); + } + + public function addContact($finalData) + { + if (empty($finalData['firstname'])) { + return ['success' => false, 'message' => __('Required field First Name is empty', 'bit-integrations'), 'code' => 400]; + } + if (empty($finalData['email'])) { + return ['success' => false, 'message' => __('Required field Email Address is empty', 'bit-integrations'), 'code' => 400]; + } + + $this->type = 'Contact'; + $this->typeName = 'Contact created'; + $formData = $this->setData($finalData); + $apiEndpoint = $this->apiUrl . '/contacts'; + + return HttpHelper::post($apiEndpoint, wp_json_encode($formData), $this->defaultHeader); + } + + public function addOpprtunity($finalData) + { + if (empty($finalData['name'])) { + return ['success' => false, 'message' => __('Required Opportunity Name is empty', 'bit-integrations'), 'code' => 400]; + } elseif (!isset($this->integrationDetails->selectedAccount) || empty($this->integrationDetails->selectedAccount)) { + return ['success' => false, 'message' => __('Required Account field is empty', 'bit-integrations'), 'code' => 400]; + } elseif (!isset($this->integrationDetails->selectedPipeline) || empty($this->integrationDetails->selectedPipeline)) { + return ['success' => false, 'message' => __('Required Pipeline field is empty', 'bit-integrations'), 'code' => 400]; + } elseif (!isset($this->integrationDetails->selectedStage) || empty($this->integrationDetails->selectedStage)) { + return ['success' => false, 'message' => __('Required Stage field is empty', 'bit-integrations'), 'code' => 400]; + } + + $this->type = 'Opportunity'; + $this->typeName = 'Opportunity created'; + $formData = $this->setData($finalData); + $formData['account'] = $this->integrationDetails->selectedAccount; + $formData['stage'] = $this->integrationDetails->selectedStage; + $apiEndpoint = $this->apiUrl . '/opportunities'; + + return HttpHelper::post($apiEndpoint, wp_json_encode($formData), $this->defaultHeader); + } + + public function generateReqDataFromFieldMap($data, $fieldMap) + { + $dataFinal = []; + foreach ($fieldMap as $value) { + $triggerValue = $value->formField; + $actionValue = $value->salesflareFormField; + $dataFinal[$actionValue] = ($triggerValue === 'custom') ? $value->customValue : $data[$triggerValue]; + } + + return $dataFinal; + } + + public function execute($fieldValues, $fieldMap, $actionName) + { + $finalData = $this->generateReqDataFromFieldMap($fieldValues, $fieldMap); + if ($actionName === 'accounts') { + $apiResponse = $this->addAccount($finalData); + } elseif ($actionName === 'contacts') { + $apiResponse = $this->addContact($finalData); + } elseif ($actionName === 'opportunities') { + $apiResponse = $this->addOpprtunity($finalData); + } + + if (isset($apiResponse->id)) { + $res = [$this->typeName . ' successfully']; + LogHandler::save($this->integrationId, wp_json_encode(['type' => $this->type, 'type_name' => $this->typeName]), 'success', wp_json_encode($res)); + } else { + LogHandler::save($this->integrationId, wp_json_encode(['type' => $this->type, 'type_name' => $this->type . ' creating']), 'error', wp_json_encode($apiResponse)); + } + + return $apiResponse; + } + + private function setData($finalData) + { + $formData = []; + $customfields = []; + $addressKeys = ['city', 'country', 'region', 'state_region', 'street', 'zip', '_dirty']; + foreach ($finalData as $key => $value) { + if (array_search($key, $addressKeys) !== false) { + $formData['address'][$key] = $value; + } elseif (strpos($key, 'custom_field_') !== false) { + $custom_key = explode('custom_field_', $key); + $customfields[$custom_key[1]] = $value; + } else { + $formData[$key] = $value; + } + } + + if (isset($this->integrationDetails->selectedTags) && !empty($this->integrationDetails->selectedTags)) { + $formData['tags'] = explode(',', $this->integrationDetails->selectedTags); + } + if (!empty($customfields)) { + $formData['custom'] = (object) $customfields; + } + + return $formData; + } +} diff --git a/backend/Actions/Salesflare/Routes.php b/backend/Actions/Salesflare/Routes.php new file mode 100644 index 000000000..f6148765a --- /dev/null +++ b/backend/Actions/Salesflare/Routes.php @@ -0,0 +1,14 @@ +actionName; if (empty($fieldMap) || empty($apiKey) || empty($actionName)) { + // translators: %s: Placeholder value return new WP_Error('REQ_FIELD_EMPTY', wp_sprintf(__('module, fields are required for %s api', 'bit-integrations'), 'Salesflare')); } diff --git a/backend/Actions/Salesforce/RecordApiHelper.php b/backend/Actions/Salesforce/RecordApiHelper.php new file mode 100644 index 000000000..fd4e6db99 --- /dev/null +++ b/backend/Actions/Salesforce/RecordApiHelper.php @@ -0,0 +1,505 @@ +_defaultHeader['Authorization'] = "Bearer {$tokenDetails->access_token}"; + $this->_defaultHeader['Content-Type'] = 'application/json'; + $this->_apiDomain = $tokenDetails->instance_url; + $this->_integrationID = $_integrationID; + } + + public function generateReqDataFromFieldMap($data, $fieldMap) + { + $dataFinal = []; + foreach ($fieldMap as $key => $value) { + $triggerValue = $value->formField; + $actionValue = $value->selesforceField; + + $dataFinal[$actionValue] = self::convertToSalesforceFormat( + $triggerValue === 'custom' && isset($value->customValue) + ? Common::replaceFieldWithValue($value->customValue, $data) + : ($data[$triggerValue] ?? null) + ); + } + + return $dataFinal; + } + + public function insertContact($finalData, $update = false) + { + $apiEndpoint = $this->_apiDomain . '/services/data/v37.0/sobjects/Contact'; + + $response = HttpHelper::post($apiEndpoint, wp_json_encode($finalData), $this->_defaultHeader); + + if (!$update) { + return $response; + } + + $filteredResult = Hooks::apply(Config::withPrefix('salesforce_update_record'), $response, $apiEndpoint, $finalData, $this->_defaultHeader); + + /** + * @deprecated 2.7.8 Use `bit_integrations_salesforce_update_record` filter instead. + * @since 2.7.8 + */ + return Hooks::apply('btcbi_salesforce_update_record', $filteredResult, $apiEndpoint, $finalData, $this->_defaultHeader); + } + + public function insertRecord($finalData, $action) + { + $apiEndpoint = $this->_apiDomain . '/services/data/v37.0/sobjects/' . $action; + + return HttpHelper::post($apiEndpoint, wp_json_encode($finalData), $this->_defaultHeader); + } + + public function insertLead($finalData, $update = false) + { + $apiEndpoint = $this->_apiDomain . '/services/data/v37.0/sobjects/Lead'; + + $response = HttpHelper::post($apiEndpoint, wp_json_encode($finalData), $this->_defaultHeader); + + if (!$update) { + return $response; + } + + $filteredResult = Hooks::apply(Config::withPrefix('salesforce_update_record'), $response, $apiEndpoint, $finalData, $this->_defaultHeader); + + /** + * @deprecated 2.7.8 Use `bit_integrations_salesforce_update_record` filter instead. + * @since 2.7.8 + */ + return Hooks::apply('btcbi_salesforce_update_record', $filteredResult, $apiEndpoint, $finalData, $this->_defaultHeader); + } + + public function createAccount($finalData) + { + $apiEndpoint = $this->_apiDomain . '/services/data/v37.0/sobjects/Account'; + + return HttpHelper::post($apiEndpoint, wp_json_encode($finalData), $this->_defaultHeader); + } + + public function createCampaign($finalData) + { + $apiEndpoint = $this->_apiDomain . '/services/data/v37.0/sobjects/Campaign'; + + return HttpHelper::post($apiEndpoint, wp_json_encode($finalData), $this->_defaultHeader); + } + + public function insertCampaignMember($campaignId, $leadId, $contactId, $statusId) + { + $apiEndpoint = $this->_apiDomain . '/services/data/v37.0/sobjects/CampaignMember'; + $finalData = [ + 'CampaignId' => $campaignId, + 'LeadId' => $leadId, + 'ContactId' => $contactId, + 'Status' => $statusId + ]; + + return HttpHelper::post($apiEndpoint, wp_json_encode($finalData), $this->_defaultHeader); + } + + public function createTask($contactId, $accountId, $subjectId, $priorityId, $statusId) + { + $apiEndpoint = $this->_apiDomain . '/services/data/v37.0/sobjects/Task'; + $finalData = [ + 'Subject' => $subjectId, + 'Priority' => $priorityId, + 'WhoId' => $contactId, + 'WhatId' => $accountId, + 'Status' => $statusId + ]; + + return HttpHelper::post($apiEndpoint, wp_json_encode($finalData), $this->_defaultHeader); + } + + public function createOpportunity($finalData, $opportunityTypeId, $opportunityStageId, $opportunityLeadSourceId, $accountId, $campaignId) + { + $apiEndpoint = $this->_apiDomain . '/services/data/v37.0/sobjects/Opportunity'; + $finalData['AccountId'] = $accountId; + $finalData['CampaignId'] = $campaignId; + $finalData['Type'] = $opportunityTypeId; + $finalData['StageName'] = $opportunityStageId; + $finalData['LeadSource'] = $opportunityLeadSourceId; + + return HttpHelper::post($apiEndpoint, wp_json_encode($finalData), $this->_defaultHeader); + } + + public function createEvent($finalData, $contactId, $accountId, $eventSubjectId) + { + $apiEndpoint = $this->_apiDomain . '/services/data/v37.0/sobjects/Event'; + $finalData['WhoId'] = $contactId; + $finalData['WhatId'] = $accountId; + $finalData['Subject'] = $eventSubjectId; + + return HttpHelper::post($apiEndpoint, wp_json_encode($finalData), $this->_defaultHeader); + } + + public function createCase($finalData, $actionsData) + { + $apiEndpoint = $this->_apiDomain . '/services/data/v37.0/sobjects/Case'; + + foreach ($actionsData as $key => $value) { + if (isset($value)) { + $finalData[$key] = $value; + } + } + + return HttpHelper::post($apiEndpoint, wp_json_encode($finalData), $this->_defaultHeader); + } + + public function execute($integrationDetails, $fieldValues, $fieldMap, $actions) + { + $actionName = $integrationDetails->actionName; + $update = isset($actions->update) ? $actions->update : false; + + if ($actionName === 'contact-create') { + $finalData = $this->generateReqDataFromFieldMap($fieldValues, $fieldMap); + $response = $this->insertContact($finalData, $update); + + $responseType = !\is_null($response) || (\is_object($response) && isset($response->id)) ? 'success' : 'error'; + $typeName = !$update || (\is_object($response) && isset($response->id)) ? 'Contact-create' : 'Contact-update'; + + $message = wp_json_encode($response); + + if ($responseType === 'success' && $update) { + $message = __('Contact Updated Successfully', 'bit-integrations'); + } elseif ($responseType === 'success') { + // translators: %s: Placeholder value + $message = wp_json_encode(wp_sprintf(__('Created contact id is : %s', 'bit-integrations'), $response->id)); + } + + LogHandler::save($this->_integrationID, wp_json_encode(['type' => 'Contact', 'type_name' => $typeName]), $responseType, $message); + } elseif ($actionName === 'lead-create') { + $finalData = $this->generateReqDataFromFieldMap($fieldValues, $fieldMap); + + $finalData = Hooks::apply(Config::withPrefix('salesforce_add_lead_utilities'), $finalData, $actions); + + /** + * @deprecated 2.7.8 Use `bit_integrations_salesforce_add_lead_utilities` filter instead. + * @since 2.7.8 + */ + $finalData = Hooks::apply('btcbi_salesforce_add_lead_utilities', $finalData, $actions); + + $insertLeadResponse = $this->insertLead($finalData, $update); + + $responseType = !\is_null($insertLeadResponse) || (\is_object($insertLeadResponse) && isset($insertLeadResponse->id)) ? 'success' : 'error'; + $typeName = !$update || (\is_object($insertLeadResponse) && isset($insertLeadResponse->id)) ? 'Lead-create' : 'Lead-update'; + + $message = wp_json_encode($insertLeadResponse); + + if ($responseType === 'success' && $update) { + $message = __('Lead Updated Successfully', 'bit-integrations'); + } elseif ($responseType === 'success') { + // translators: %s: Placeholder value + $message = wp_json_encode(wp_sprintf(__('Created lead id is : %s', 'bit-integrations'), $insertLeadResponse->id)); + } + + LogHandler::save($this->_integrationID, wp_json_encode(['type' => 'Lead', 'type_name' => $typeName]), $responseType, $message); + } elseif ($actionName === 'account-create') { + $finalData = $this->generateReqDataFromFieldMap($fieldValues, $fieldMap); + + if (isset($actions->selectedAccType)) { + $finalData['Type'] = $actions->selectedAccType; + } + if (isset($actions->selectedOwnership)) { + $finalData['Ownership'] = $actions->selectedOwnership; + } + + $createAccountResponse = $this->createAccount($finalData); + + if (\is_object($createAccountResponse) && property_exists($createAccountResponse, 'id')) { + // translators: %s: Placeholder value + LogHandler::save($this->_integrationID, wp_json_encode(['type' => 'Account', 'type_name' => 'Account-create']), 'success', wp_json_encode(wp_sprintf(__('Created account id is : %s', 'bit-integrations'), $createAccountResponse->id))); + } else { + LogHandler::save($this->_integrationID, wp_json_encode(['type' => 'Account', 'type_name' => 'Account-create']), 'error', wp_json_encode($createAccountResponse)); + } + } elseif ($actionName === 'campaign-create') { + $finalData = $this->generateReqDataFromFieldMap($fieldValues, $fieldMap); + $insertCampaignResponse = $this->createCampaign($finalData); + if (\is_object($insertCampaignResponse) && property_exists($insertCampaignResponse, 'id')) { + // translators: %s: Placeholder value + LogHandler::save($this->_integrationID, wp_json_encode(['type' => 'Campaign', 'type_name' => 'Campaign-create']), 'success', wp_json_encode(wp_sprintf(__('Created campaign id is : %s', 'bit-integrations'), $insertCampaignResponse->id))); + } else { + LogHandler::save($this->_integrationID, wp_json_encode(['type' => 'Campaign', 'type_name' => 'Campaign-create']), 'error', wp_json_encode($insertCampaignResponse)); + } + } elseif ($actionName === 'add-campaign-member') { + $campaignId = $integrationDetails->campaignId; + $leadId = isset($integrationDetails->leadId) ? $integrationDetails->leadId : null; + $contactId = isset($integrationDetails->contactId) ? $integrationDetails->contactId : null; + $statusId = isset($integrationDetails->statusId) ? $integrationDetails->statusId : null; + $insertCampaignMember = $this->insertCampaignMember($campaignId, $leadId, $contactId, $statusId); + if (\is_object($insertCampaignMember) && property_exists($insertCampaignMember, 'id')) { + // translators: %s: Placeholder value + LogHandler::save($this->_integrationID, wp_json_encode(['type' => 'CampaignMember', 'type_name' => 'CampaignMember-create']), 'success', wp_json_encode(wp_sprintf(__('Created campaign member id is : %s', 'bit-integrations'), $insertCampaignMember->id))); + } else { + LogHandler::save($this->_integrationID, wp_json_encode(['type' => 'CampaignMember', 'type_name' => 'CampaignMember-create']), 'error', wp_json_encode($insertCampaignMember)); + } + } elseif ($actionName === 'task-create') { + $contactId = isset($integrationDetails->contactId) ? $integrationDetails->contactId : null; + $accountId = isset($integrationDetails->accountId) ? $integrationDetails->accountId : null; + $subjectId = $integrationDetails->subjectId; + $priorityId = $integrationDetails->priorityId; + $statusId = isset($integrationDetails->statusId) ? $integrationDetails->statusId : null; + $apiResponse = $this->createTask($contactId, $accountId, $subjectId, $priorityId, $statusId); + if (\is_object($apiResponse) && property_exists($apiResponse, 'id')) { + // translators: %s: Placeholder value + LogHandler::save($this->_integrationID, wp_json_encode(['type' => 'Task', 'type_name' => 'Task-create']), 'success', wp_json_encode(wp_sprintf(__('Created task id is : %s', 'bit-integrations'), $apiResponse->id))); + } else { + LogHandler::save($this->_integrationID, wp_json_encode(['type' => 'Task', 'type_name' => 'Task-create']), 'error', wp_json_encode($apiResponse)); + } + } elseif ($actionName === 'opportunity-create') { + $opportunityTypeId = isset($actions->opportunityTypeId) ? $actions->opportunityTypeId : null; + $opportunityStageId = isset($actions->opportunityStageId) ? $actions->opportunityStageId : null; + $opportunityLeadSourceId = isset($actions->opportunityLeadSourceId) ? $actions->opportunityLeadSourceId : null; + $accountId = isset($actions->accountId) ? $actions->accountId : null; + $campaignId = isset($actions->campaignId) ? $actions->campaignId : null; + $finalData = $this->generateReqDataFromFieldMap($fieldValues, $fieldMap); + $opportunityResponse = $this->createOpportunity($finalData, $opportunityTypeId, $opportunityStageId, $opportunityLeadSourceId, $accountId, $campaignId); + if (\is_object($opportunityResponse) && property_exists($opportunityResponse, 'id')) { + // translators: %s: Placeholder value + LogHandler::save($this->_integrationID, wp_json_encode(['type' => 'Opportunity', 'type_name' => 'Opportunity-create']), 'success', wp_json_encode(wp_sprintf(__('Created opportunity id is : %s', 'bit-integrations'), $opportunityResponse->id))); + } else { + LogHandler::save($this->_integrationID, wp_json_encode(['type' => 'Opportunity', 'type_name' => 'Opportunity-create']), 'error', wp_json_encode($opportunityResponse)); + } + } elseif ($actionName === 'event-create') { + $contactId = isset($actions->contactId) ? $actions->contactId : null; + $accountId = isset($actions->accountId) ? $actions->accountId : null; + $eventSubjectId = isset($actions->eventSubjectId) ? $actions->eventSubjectId : null; + $finalData = $this->generateReqDataFromFieldMap($fieldValues, $fieldMap); + $createEventResponse = $this->createEvent($finalData, $contactId, $accountId, $eventSubjectId); + if (\is_object($createEventResponse) && property_exists($createEventResponse, 'id')) { + // translators: %s: Placeholder value + LogHandler::save($this->_integrationID, wp_json_encode(['type' => 'Event', 'type_name' => 'Event-create']), 'success', wp_json_encode(wp_sprintf(__('Created event id is : %s', 'bit-integrations'), $createEventResponse->id))); + } else { + LogHandler::save($this->_integrationID, wp_json_encode(['type' => 'Event', 'type_name' => 'Event-create']), 'error', wp_json_encode($createEventResponse)); + } + } elseif ($actionName === 'case-create') { + $actionsData['ContactId'] = isset($actions->contactId) ? $actions->contactId : null; + $actionsData['AccountId'] = isset($actions->accountId) ? $actions->accountId : null; + $actionsData['Status'] = isset($actions->caseStatusId) ? $actions->caseStatusId : null; + $actionsData['Origin'] = isset($actions->caseOriginId) ? $actions->caseOriginId : null; + $actionsData['Priority'] = isset($actions->casePriorityId) ? $actions->casePriorityId : null; + $actionsData['Reason'] = isset($actions->caseReason) ? $actions->caseReason : null; + $actionsData['Type'] = isset($actions->caseType) ? $actions->caseType : null; + $actionsData['PotentialLiability__c'] = isset($actions->potentialLiabilityId) ? $actions->potentialLiabilityId : null; + $actionsData['SLAViolation__c'] = isset($actions->slaViolationId) ? $actions->slaViolationId : null; + + $finalData = $this->generateReqDataFromFieldMap($fieldValues, $fieldMap); + $createCaseResponse = $this->createCase($finalData, $actionsData); + + if (\is_object($createCaseResponse) && property_exists($createCaseResponse, 'id')) { + // translators: %s: Placeholder value + LogHandler::save($this->_integrationID, wp_json_encode(['type' => 'Case', 'type_name' => 'Case-create']), 'success', wp_json_encode(wp_sprintf(__('Created case id is : %s', 'bit-integrations'), $createCaseResponse->id))); + } else { + LogHandler::save($this->_integrationID, wp_json_encode(['type' => 'Case', 'type_name' => 'Case-create']), 'error', wp_json_encode($createCaseResponse)); + } + } else { + $finalData = $this->generateReqDataFromFieldMap($fieldValues, $fieldMap); + $insertContactResponse = $this->insertRecord($finalData, $actionName); + if (\is_object($insertContactResponse) && property_exists($insertContactResponse, 'id')) { + LogHandler::save($this->_integrationID, wp_json_encode(['type' => $actionName, 'type_name' => $actionName . '-create']), 'success', wp_json_encode("{$actionName} id is : {$insertContactResponse->id}")); + } else { + LogHandler::save($this->_integrationID, wp_json_encode(['type' => $actionName, 'type_name' => $actionName . '-create']), 'error', wp_json_encode($insertContactResponse)); + } + } + + return true; + } + + public static function convertToSalesforceFormat($input) + { + try { + if (!$input || !\is_string($input)) { + return $input; + } + + $input = trim($input); + + if (empty($input)) { + return $input; + } + + // ---------------------------- + // PHONE NUMBER PROTECTION + // ---------------------------- + if (preg_match('/^\+?\d{7,15}$/', $input)) { + return $input; + } + + // ------------------------------------------------------------ + // 1) Handle UNIX timestamps (10 or 13 digits) + // ------------------------------------------------------------ + if (self::validateNumericDateWithLength($input, 10)) { + return gmdate('Y-m-d\TH:i:s\Z', (int) $input); + } + if (self::validateNumericDateWithLength($input, 13)) { + return gmdate('Y-m-d\TH:i:s\Z', (int) ($input / 1000)); + } + + // ------------------------------------------------------------ + // 2) Natural-language dates ("today", "tomorrow", "next Monday", etc.) + // ------------------------------------------------------------ + if (preg_match('/^[a-zA-Z ]+$/', $input) || str_contains($input, 'ago')) { + $ts = strtotime($input); + if ($ts) { + return gmdate('Y-m-d', $ts); + } + } + + // ------------------------------------------------------------ + // 3) Clean ordinals: 1st, 2nd, 3rd, 21st, 31st... + // ------------------------------------------------------------ + $clean = preg_replace('/\b(\d+)(st|nd|rd|th)\b/i', '$1', $input); + + // ------------------------------------------------------------ + // 4) Japanese/Chinese/Korean locale replacements + // ------------------------------------------------------------ + $clean = str_replace( + ['年', '月', '日', '년', '월', '일'], + ['-', '-', '', '-', '-', ''], + $clean + ); + + // ------------------------------------------------------------ + // 5) Week-based formats (2025-W05 or 2025-W05-6) + // ------------------------------------------------------------ + if (preg_match('/^(\d{4})-?W(\d{2})(?:-?(\d))?$/i', $clean, $m)) { + $year = $m[1]; + $week = $m[2]; + $day = $m[3] ?? 1; + + try { + $dt = new DateTime("{$year}-W{$week}-{$day}", new DateTimeZone('UTC')); + + return $dt->format('Y-m-d'); + } catch (Throwable $e) { + } + } + + // ------------------------------------------------------------ + // 6) Quarter formats (Q1 2025, 2025 Q1, 1st Quarter 2025) + // ------------------------------------------------------------ + if (preg_match('/(Q[1-4]|[1-4]st Quarter)\s*[, ]*\s*(\d{4})/i', $clean, $m)) { + $q = preg_replace('/\D/', '', $m[1]); // Extract 1–4 + $year = $m[2]; + $month = (($q - 1) * 3) + 1; + + return "{$year}-" . str_pad($month, 2, '0', STR_PAD_LEFT) . '-01'; + } + + // ------------------------------------------------------------ + // 7) Compact numeric formats (01022025, 20250201, 250201, 010225) + // ------------------------------------------------------------ + if (self::validateNumericDateWithLength($clean, 8)) { + // YYYYMMDD or DDMMYYYY or MMDDYYYY → try multiple interpretations + $candidates = [ + substr($clean, 0, 4) . '-' . substr($clean, 4, 2) . '-' . substr($clean, 6, 2), // YMD + substr($clean, 4, 4) . '-' . substr($clean, 2, 2) . '-' . substr($clean, 0, 2), // DMY + ]; + foreach ($candidates as $c) { + if (strtotime($c)) { + return $c; + } + } + } + + if (self::validateNumericDateWithLength($clean, 6)) { + // DDMMYY / YYMMDD / MMDDYY + $yy = \intval(substr($clean, -2)); + + // Sliding window: interpret two-digit year as closest to current year within 50 years + $currentYear = \intval(gmdate('Y')); + $century = \intval($currentYear / 100) * 100; + $fullYear = $century + $yy; + $window = 50; + + if ($fullYear < $currentYear - $window) { + $fullYear += 100; + } elseif ($fullYear > $currentYear + $window) { + $fullYear -= 100; + } + + $dm = substr($clean, 0, 2) . '-' . substr($clean, 2, 2) . '-' . $fullYear; + $md = substr($clean, 2, 2) . '-' . substr($clean, 0, 2) . '-' . $fullYear; + + foreach ([$dm, $md] as $c) { + $ts = strtotime($c); + if ($ts) { + return gmdate('Y-m-d', $ts); + } + } + } + + // ------------------------------------------------------------ + // 8) Try direct DateTime parsing for most formats + // ------------------------------------------------------------ + $dt = self::tryParseDateTimeImmutable($clean); + + if ($dt instanceof DateTimeImmutable) { + // Detect if datetime or pure date + if (preg_match('/\d{1,2}:\d/', $clean)) { + return $dt->setTimezone(new DateTimeZone('UTC')) + ->format('Y-m-d\TH:i:s\Z'); + } + + return $dt->format('Y-m-d'); + } + + // ------------------------------------------------------------ + // 9) Last fallback using strtotime() + // ------------------------------------------------------------ + $ts = strtotime($clean); + if ($ts) { + // Detect datetime or date-only + if (preg_match('/\d{1,2}:\d/', $clean)) { + return gmdate('Y-m-d\TH:i:s\Z', $ts); + } + + return gmdate('Y-m-d', $ts); + } + + // ------------------------------------------------------------ + // 10) No match → return original + // ------------------------------------------------------------ + return $input; + } catch (Throwable $th) { + return $input; + } + } + + private static function tryParseDateTimeImmutable($value) + { + try { + return new DateTimeImmutable($value); + } catch (Throwable $e) { + return; + } + } + + private static function validateNumericDateWithLength($input, $length) + { + return is_numeric($input) && \strlen($input) === $length; + } +} diff --git a/backend/Actions/Salesforce/Routes.php b/backend/Actions/Salesforce/Routes.php new file mode 100644 index 000000000..f2d62cc17 --- /dev/null +++ b/backend/Actions/Salesforce/Routes.php @@ -0,0 +1,28 @@ +integrationDetails = $integrationDetails; + $this->integrationId = $integId; + $this->apiUrl = "https://{$linkName}.salesmate.io/apis/"; + $this->defaultHeader + = [ + 'Content-type' => 'application/json', + 'accessToken' => $sessionToken, + 'x-linkname' => $linkName . '.salesmate.io', + ]; + } + + public function addProduct($finalData) + { + if (empty($finalData['name'])) { + return ['success' => false, 'message' => __('Required field Name is empty', 'bit-integrations'), 'code' => 400]; + } elseif (empty($finalData['unitPrice'])) { + return ['success' => false, 'message' => __('Required field unit Price is empty', 'bit-integrations'), 'code' => 400]; + } + + $finalData['isActive'] = isset($this->integrationDetails->selectedIsActive) && !empty($this->integrationDetails->selectedIsActive) ? $this->integrationDetails->selectedIsActive : 1; + $finalData['currency'] = isset($this->integrationDetails->selectedCurrency) && !empty($this->integrationDetails->selectedCurrency) ? $this->integrationDetails->selectedCurrency : 'USD'; + + if (isset($this->integrationDetails->selectedTag)) { + $finalData['tags'] = ($this->integrationDetails->selectedTag); + } + if (isset($this->integrationDetails->selectedCRMOwner)) { + $finalData['owner'] = ($this->integrationDetails->selectedCRMOwner); + } + + $this->type = 'Product'; + $this->typeName = 'Product created'; + $apiEndpoint = $this->apiUrl . 'v1/products'; + + return HttpHelper::post($apiEndpoint, wp_json_encode($finalData), $this->defaultHeader); + } + + public function addContact($finalData) + { + if (empty($finalData['lastName'])) { + return ['success' => false, 'message' => __('Required field lastName is empty', 'bit-integrations'), 'code' => 400]; + } + + if (isset($this->integrationDetails->selectedTag) && !empty($this->integrationDetails->selectedTag)) { + $finalData['tags'] = ($this->integrationDetails->selectedTag); + } + if (isset($this->integrationDetails->selectedType) && !empty($this->integrationDetails->selectedType)) { + $finalData['type'] = ($this->integrationDetails->selectedType); + } + if (isset($this->integrationDetails->selectedCRMOwner) && !empty($this->integrationDetails->selectedCRMOwner)) { + $finalData['owner'] = ($this->integrationDetails->selectedCRMOwner); + } + + $this->type = 'Contact'; + $this->typeName = 'Contact created'; + $apiEndpoint = $this->apiUrl . 'contact/v4'; + + return HttpHelper::post($apiEndpoint, wp_json_encode($finalData), $this->defaultHeader); + } + + public function addCompany($finalData) + { + if (empty($finalData['name'])) { + return ['success' => false, 'message' => __('Required field name is empty', 'bit-integrations'), 'code' => 400]; + } + + $finalData['currency'] = isset($this->integrationDetails->selectedCurrency) && !empty($this->integrationDetails->selectedCurrency) ? $this->integrationDetails->selectedCurrency : 'USD'; + + if (isset($this->integrationDetails->selectedTag) && !empty($this->integrationDetails->selectedTag)) { + $finalData['tags'] = ($this->integrationDetails->selectedTag); + } + if (isset($this->integrationDetails->selectedType) && !empty($this->integrationDetails->selectedType)) { + $finalData['type'] = ($this->integrationDetails->selectedType); + } + if (isset($this->integrationDetails->selectedCRMOwner) && !empty($this->integrationDetails->selectedCRMOwner)) { + $finalData['owner'] = ($this->integrationDetails->selectedCRMOwner); + } + + $this->type = 'Company'; + $this->typeName = 'Company created'; + $apiEndpoint = $this->apiUrl . 'company/v4'; + + return HttpHelper::post($apiEndpoint, wp_json_encode($finalData), $this->defaultHeader); + } + + public function addDeal($finalData) + { + if (empty($finalData['title'])) { + return ['success' => false, 'message' => __('Required field title is empty', 'bit-integrations'), 'code' => 400]; + } + + $finalData['currency'] = isset($this->integrationDetails->selectedCurrency) && !empty($this->integrationDetails->selectedCurrency) ? $this->integrationDetails->selectedCurrency : 'USD'; + $finalData['status'] = isset($this->integrationDetails->selectedStatus) && !empty($this->integrationDetails->selectedStatus) ? $this->integrationDetails->selectedStatus : 'Open'; + $finalData['source'] = isset($this->integrationDetails->selectedSource) && !empty($this->integrationDetails->selectedSource) ? $this->integrationDetails->selectedSource : 'Ads'; + $finalData['priority'] = isset($this->integrationDetails->selectedPriority) && !empty($this->integrationDetails->selectedPriority) ? $this->integrationDetails->selectedPriority : 'High'; + + if (isset($this->integrationDetails->selectedTag) && !empty($this->integrationDetails->selectedTag)) { + $finalData['tags'] = ($this->integrationDetails->selectedTag); + } + if (isset($this->integrationDetails->selectedType) && !empty($this->integrationDetails->selectedType)) { + $finalData['type'] = ($this->integrationDetails->selectedType); + } + if (isset($this->integrationDetails->selectedCRMContact) && !empty($this->integrationDetails->selectedCRMContact)) { + $finalData['primaryContact'] = ($this->integrationDetails->selectedCRMContact); + } + if (isset($this->integrationDetails->selectedCRMOwner) && !empty($this->integrationDetails->selectedCRMOwner)) { + $finalData['owner'] = ($this->integrationDetails->selectedCRMOwner); + } + if (isset($this->integrationDetails->selectedCRMPipeline) && !empty($this->integrationDetails->selectedCRMPipeline)) { + $finalData['pipeline'] = ($this->integrationDetails->selectedCRMPipeline); + } + if (isset($this->integrationDetails->selectedCRMStage) && !empty($this->integrationDetails->selectedCRMStage)) { + $finalData['stage'] = ($this->integrationDetails->selectedCRMStage); + } + + $this->type = 'Deal'; + $this->typeName = 'Deal created'; + $apiEndpoint = $this->apiUrl . 'deal/v4'; + + return HttpHelper::post($apiEndpoint, wp_json_encode($finalData), $this->defaultHeader); + } + + public function generateReqDataFromFieldMap($data, $fieldMap) + { + $dataFinal = []; + foreach ($fieldMap as $value) { + $triggerValue = $value->formField; + $actionValue = $value->salesmateFormField; + $dataFinal[$actionValue] = ($triggerValue === 'custom') ? $value->customValue : $data[$triggerValue]; + } + + return $dataFinal; + } + + public function execute($fieldValues, $fieldMap, $actionId) + { + $finalData = $this->generateReqDataFromFieldMap($fieldValues, $fieldMap); + if ((int) $actionId === 1) { + $apiResponse = $this->addContact($finalData); + } elseif ((int) $actionId === 4) { + $apiResponse = $this->addDeal($finalData); + } elseif ((int) $actionId === 5) { + $apiResponse = $this->addCompany($finalData); + } elseif ((int) $actionId === 6) { + $apiResponse = $this->addProduct($finalData); + } + + if ($apiResponse->Status === 'success') { + $res = [$this->typeName . ' successfully']; + LogHandler::save($this->integrationId, wp_json_encode(['type' => $this->type, 'type_name' => $this->typeName]), 'success', wp_json_encode($res)); + } else { + LogHandler::save($this->integrationId, wp_json_encode(['type' => $this->type, 'type_name' => $this->type . ' creating']), 'error', wp_json_encode($apiResponse)); + } + + return $apiResponse; + } +} diff --git a/backend/Actions/Salesmate/Routes.php b/backend/Actions/Salesmate/Routes.php new file mode 100644 index 000000000..a39b62e9d --- /dev/null +++ b/backend/Actions/Salesmate/Routes.php @@ -0,0 +1,18 @@ +link_name; if (empty($fieldMap) || empty($sessionToken) || empty($actionName) || empty($actionId) || empty($linkName)) { + // translators: %s: Placeholder value return new WP_Error('REQ_FIELD_EMPTY', wp_sprintf(__('module, fields are required for %s api', 'bit-integrations'), 'Salesmate')); } diff --git a/backend/Actions/Selzy/RecordApiHelper.php b/backend/Actions/Selzy/RecordApiHelper.php new file mode 100644 index 000000000..440ba5b97 --- /dev/null +++ b/backend/Actions/Selzy/RecordApiHelper.php @@ -0,0 +1,131 @@ +_integrationDetails = $integrationDetails; + $this->_integrationID = $integId; + } + + public function generateReqDataFromFieldMap($data, $field_map) + { + $dataFinal = []; + + foreach ($field_map as $value) { + $triggerValue = $value->formField; + $actionValue = $value->selzyFormField; + if ($triggerValue === 'custom') { + $dataFinal[$actionValue] = Common::replaceFieldWithValue($value->customValue, $data); + } elseif (!\is_null($data[$triggerValue])) { + $dataFinal[$actionValue] = $data[$triggerValue]; + } + } + + return $dataFinal; + } + + public function response($status, $code, $type, $typeName, $apiResponse) + { + $res = ['success' => $code === 200 ? true : false, 'message' => $apiResponse, 'code' => $code]; + LogHandler::save($this->_integrationID, wp_json_encode(['type' => $type, 'type_name' => $typeName]), $status, wp_json_encode($res)); + + return $res; + } + + public function subscribe($authKey, $listIds, $tags, $option, $overwrite, $formData) + { + $data = ''; + foreach ($formData as $key => $field) { + $field = str_replace(' ', '+', $field); + $data .= "&fields[{$key}]={$field}"; + } + $query = http_build_query( + [ + 'format' => 'json', + 'api_key' => $authKey, + 'list_ids' => $listIds, + 'tags' => $tags, + 'double_optin' => $option, + 'overwrite' => $overwrite + ] + ); + + $apiEndpoint = "{$this->baseUrl}subscribe?" . $query . $data; + + $headers = [ + 'Content-Type' => 'application/json' + ]; + + return HttpHelper::post($apiEndpoint, null, $headers); + } + + public function unsubscribe($authKey, $listIds, $formData) + { + $apiEndpoints = "{$this->baseUrl}exclude?format=json&api_key={$authKey}&list_ids={$listIds}&contact_type=email&contact={$formData->email}"; + $headers = [ + 'Content-Type' => 'application/json' + ]; + + return HttpHelper::post($apiEndpoints, null, $headers); + } + + public function execute( + $method, + $listIds, + $tags, + $option, + $overwrite, + $fieldValues, + $field_map, + $authKey + ) { + $finalData = (object) $this->generateReqDataFromFieldMap($fieldValues, $field_map); + $type_name = 'add-contact'; + if ($overwrite == 1) { + $type_name = 'contact-overwrite'; + } + if ($overwrite == 2) { + $type_name = 'contact-tag-overwrite'; + } + switch ($method) { + case 1: + $apiResponse = $this->subscribe($authKey, $listIds, $tags, $option, $overwrite, $finalData); + if (!$apiResponse->result) { + $this->response('error', 400, 'subscribe', $type_name, $apiResponse); + } else { + $this->response('success', 200, 'subscribe', $type_name, $apiResponse); + } + + break; + case 2: + $apiResponse = $this->unsubscribe($authKey, $listIds, $finalData); + if (!$apiResponse->result) { + $this->response('error', 400, 'unsubscribe', 'remove-contact', $apiResponse); + } else { + $this->response('success', 200, 'unsubscribe', 'remove-contact', $apiResponse); + } + + break; + } + + return $apiResponse; + } +} diff --git a/backend/Actions/Selzy/Routes.php b/backend/Actions/Selzy/Routes.php new file mode 100644 index 000000000..646082810 --- /dev/null +++ b/backend/Actions/Selzy/Routes.php @@ -0,0 +1,12 @@ +_integrationDetails = $integrationDetails; + $this->_integrationID = $integId; + } + + public function addContact($access_token, $listId, $finalData) + { + $apiEndpoints = 'https://api.sendfox.com/contacts'; + $listId = explode(',', $listId); + $header = [ + 'Authorization' => "Bearer {$access_token}", + 'Accept' => 'application/json', + ]; + + $data = [ + 'email' => $finalData['email'], + 'first_name' => $finalData['first_name'], + 'last_name' => $finalData['last_name'], + 'lists' => $listId, + ]; + + return HttpHelper::post($apiEndpoints, $data, $header); + } + + public function createContactList($access_token, $finalData) + { + $apiEndpoints = 'https://api.sendfox.com/lists'; + + $header = [ + 'Authorization' => "Bearer {$access_token}", + 'Accept' => 'application/json', + ]; + + $data = [ + 'name' => $finalData['name'], + ]; + + return HttpHelper::post($apiEndpoints, $data, $header); + } + + public function generateReqDataFromFieldMap($data, $fieldMap) + { + $dataFinal = []; + + foreach ($fieldMap as $key => $value) { + $triggerValue = $value->formField; + $actionValue = $value->sendFoxFormField; + if ($triggerValue === 'custom') { + $dataFinal[$actionValue] = Common::replaceFieldWithValue($value->customValue, $data); + } elseif (!\is_null($data[$triggerValue])) { + $dataFinal[$actionValue] = $data[$triggerValue]; + } + } + + return $dataFinal; + } + + public function generateListReqDataFromFieldMap($data, $fieldMap) + { + $dataFinal = []; + + foreach ($fieldMap as $key => $value) { + $triggerValue = $value->formField; + $actionValue = $value->sendFoxListFormField; + if ($triggerValue === 'custom') { + $dataFinal[$actionValue] = Common::replaceFieldWithValue($value->customValue, $data); + } elseif (!\is_null($data[$triggerValue])) { + $dataFinal[$actionValue] = $data[$triggerValue]; + } + } + + return $dataFinal; + } + + public function generateReqUnsubscribeDataFromFieldMap($data, $fieldMap) + { + $dataFinal = []; + + foreach ($fieldMap as $key => $value) { + $triggerValue = $value->formField; + $actionValue = $value->sendFoxUnsubscribeFormField; + if ($triggerValue === 'custom') { + $dataFinal[$actionValue] = Common::replaceFieldWithValue($value->customValue, $data); + } elseif (!\is_null($data[$triggerValue])) { + $dataFinal[$actionValue] = $data[$triggerValue]; + } + } + + return $dataFinal; + } + + public function unsubscribeContact($access_token, $finalData) + { + $apiEndpoints = 'https://api.sendfox.com/unsubscribe'; + + $header = [ + 'Authorization' => "Bearer {$access_token}", + 'Accept' => 'application/json', + ]; + + $data = [ + 'email' => $finalData['email'], + ]; + + return HttpHelper::request($apiEndpoints, 'PATCH', $data, $header); + } + + public function execute( + $listId, + $fieldValues, + $fieldMap, + $access_token, + $integrationDetails + ) { + $fieldData = []; + if ($integrationDetails->mainAction === '1') { + $type_name = 'Create List'; + $finalData = $this->generateListReqDataFromFieldMap($fieldValues, $integrationDetails->field_map_list); + $apiResponseList = $this->createContactList($access_token, $finalData); + + if (property_exists($apiResponseList, 'id')) { + LogHandler::save($this->_integrationID, wp_json_encode(['type' => 'record', 'type_name' => $type_name]), 'success', wp_json_encode($apiResponseList)); + } else { + LogHandler::save($this->_integrationID, wp_json_encode(['type' => 'record', 'type_name' => $type_name]), 'error', wp_json_encode($apiResponseList)); + } + } + if ($integrationDetails->mainAction === '2') { + $type_name = 'Create Contact'; + $finalData = $this->generateReqDataFromFieldMap($fieldValues, $fieldMap); + $apiResponse = $this->addContact($access_token, $listId, $finalData); + if (property_exists($apiResponse, 'errors')) { + LogHandler::save($this->_integrationID, wp_json_encode(['type' => 'contact', 'type_name' => $type_name]), 'error', wp_json_encode($apiResponse)); + } else { + LogHandler::save($this->_integrationID, wp_json_encode(['type' => 'record', 'type_name' => $type_name]), 'success', wp_json_encode($apiResponse)); + } + } + + if ($integrationDetails->mainAction === '3') { + $type_name = 'Unsubscribe'; + $finalData = $this->generateReqUnsubscribeDataFromFieldMap($fieldValues, $integrationDetails->field_map_unsubscribe); + $apiResponse = $this->unsubscribeContact($access_token, $finalData); + if (property_exists($apiResponse, 'id')) { + LogHandler::save($this->_integrationID, wp_json_encode(['type' => 'contact', 'type_name' => $type_name]), 'success', wp_json_encode($apiResponse)); + } else { + LogHandler::save($this->_integrationID, wp_json_encode(['type' => 'record', 'type_name' => $type_name]), 'error', wp_json_encode($apiResponse)); + } + } + + return $apiResponse; + } +} diff --git a/backend/Actions/SendFox/Routes.php b/backend/Actions/SendFox/Routes.php new file mode 100644 index 000000000..dc5aeb632 --- /dev/null +++ b/backend/Actions/SendFox/Routes.php @@ -0,0 +1,11 @@ +_integrationDetails = $integrationDetails; + $this->_integrationID = $integId; + $this->_defaultHeader = [ + 'Authorization' => 'Bearer ' . $integrationDetails->apiKey, + 'Content-Type' => 'application/json' + ]; + } + + public function addContact($selectedLists, $finalData) + { + $apiEndpoints = 'https://api.sendgrid.com/v3/marketing/contacts'; + + if (empty($finalData['email'])) { + return ['success' => false, 'message' => __('Required field Email is empty', 'bit-integrations'), 'code' => 400]; + } + + $this->_responseType = $this->isExist($finalData['email']); + + if (!empty($selectedLists)) { + $lists = explode(',', $selectedLists); + $requestParams['list_ids'] = $lists; + } + + $staticFieldsKeys = [ + 'email', 'first_name', 'last_name', 'alternate_emails', 'address_line_1', 'address_line_2', 'city', + 'state_province_region', 'postal_code', 'country', 'phone_number', 'whatsapp', 'line', 'facebook', 'unique_name' + ]; + + foreach ($finalData as $key => $value) { + if (\in_array($key, $staticFieldsKeys)) { + $contacts[$key] = $value; + } else { + $customFields[$key] = $value; + } + } + + if (!empty($customFields)) { + $contacts['custom_fields'] = (object) $customFields; + } + + $requestParams['contacts'][] = (object) $contacts; + + return HttpHelper::request($apiEndpoints, 'PUT', wp_json_encode($requestParams), $this->_defaultHeader); + } + + public function generateReqDataFromFieldMap($data, $fieldMap) + { + $dataFinal = []; + foreach ($fieldMap as $value) { + $triggerValue = $value->formField; + $actionValue = $value->sendGridFormField; + if ($triggerValue === 'custom') { + $dataFinal[$actionValue] = Common::replaceFieldWithValue($value->customValue, $data); + } elseif (!\is_null($data[$triggerValue])) { + $dataFinal[$actionValue] = $data[$triggerValue]; + } + } + + return $dataFinal; + } + + public function execute($selectedLists, $fieldValues, $fieldMap) + { + $finalData = $this->generateReqDataFromFieldMap($fieldValues, $fieldMap); + $apiResponse = $this->addContact($selectedLists, $finalData); + + if (!isset($apiResponse->errors)) { + $res = ['message' => 'Contact ' . $this->_responseType . ' successfully']; + LogHandler::save($this->_integrationID, wp_json_encode(['type' => 'contact', 'type_name' => 'Contact ' . $this->_responseType]), 'success', wp_json_encode($res)); + } else { + LogHandler::save($this->_integrationID, wp_json_encode(['type' => '', 'type_name' => 'Adding contact']), 'error', wp_json_encode($apiResponse)); + } + + return $apiResponse; + } + + private function isExist($email) + { + $apiEndpoint = 'https://api.sendgrid.com/v3/marketing/contacts/search/emails'; + $emails['emails'] = (array) $email; + $response = HttpHelper::post($apiEndpoint, wp_json_encode($emails), $this->_defaultHeader); + + return empty($response) ? 'created' : 'updated'; + } +} diff --git a/backend/Actions/SendGrid/Routes.php b/backend/Actions/SendGrid/Routes.php new file mode 100644 index 000000000..f639a9bde --- /dev/null +++ b/backend/Actions/SendGrid/Routes.php @@ -0,0 +1,11 @@ +field_map; if (empty($fieldMap) || empty($apiKey)) { + // translators: %s: Placeholder value return new WP_Error('REQ_FIELD_EMPTY', wp_sprintf(__('module, fields are required for %s api', 'bit-integrations'), 'SendGrid')); } diff --git a/backend/Actions/SendPulse/RecordApiHelper.php b/backend/Actions/SendPulse/RecordApiHelper.php new file mode 100644 index 000000000..7428b2a8e --- /dev/null +++ b/backend/Actions/SendPulse/RecordApiHelper.php @@ -0,0 +1,76 @@ +_integrationDetails = $integrationDetails; + $this->_integrationID = $integId; + $this->_defaultHeader = [ + 'Authorization' => 'Bearer ' . $access_token, + 'Content-Type' => 'application/json' + ]; + } + + public function addContact($selectedList, $finalData) + { + $apiEndpoints = "https://api.sendpulse.com/addressbooks/{$selectedList}/emails"; + + $variables = array_filter($finalData, fn ($key) => $key !== 'email', ARRAY_FILTER_USE_KEY); + + $body = [ + 'emails' => [ + [ + 'email' => $finalData['email'], + 'variables' => $variables + ] + ] + ]; + + return HttpHelper::post($apiEndpoints, wp_json_encode($body), $this->_defaultHeader); + } + + public function generateReqDataFromFieldMap($data, $fieldMap) + { + $dataFinal = []; + foreach ($fieldMap as $value) { + $triggerValue = $value->formField; + $actionValue = $value->sendPulseField; + + if ($triggerValue === 'custom') { + $dataFinal[$actionValue] = $value->customValue; + } elseif (!\is_null($data[$triggerValue])) { + $dataFinal[$actionValue] = $data[$triggerValue]; + } + } + + return $dataFinal; + } + + public function execute($selectedList, $fieldValues, $fieldMap) + { + $finalData = $this->generateReqDataFromFieldMap($fieldValues, $fieldMap); + + $apiResponse = $this->addContact($selectedList, $finalData); + + if ($apiResponse->result == true) { + $res = ['message' => __('Contact Added Successfully', 'bit-integrations')]; + LogHandler::save($this->_integrationID, wp_json_encode(['type' => 'contact', 'type_name' => 'Contact added']), 'success', wp_json_encode($res)); + } else { + LogHandler::save($this->_integrationID, wp_json_encode(['type' => 'contact', 'type_name' => 'Adding Contact']), 'error', wp_json_encode($apiResponse)); + } + + return $apiResponse; + } +} diff --git a/backend/Actions/SendPulse/Routes.php b/backend/Actions/SendPulse/Routes.php new file mode 100644 index 000000000..a7aa12caa --- /dev/null +++ b/backend/Actions/SendPulse/Routes.php @@ -0,0 +1,12 @@ + ['fieldValue' => 'phone', 'fieldName' => __('Phone', 'bit-integrations'), 'required' => false] ]; - if (Helper::proActionFeatExists('SendPulse', 'refreshFields')) { - $apiEndpoint = "https://api.sendpulse.com/addressbooks/{$requestParams->list_id}/variables"; + $apiEndpoint = "https://api.sendpulse.com/addressbooks/{$requestParams->list_id}/variables"; - $token = self::tokenExpiryCheck($requestParams->tokenDetails, $requestParams->client_id, $requestParams->client_secret); + $token = self::tokenExpiryCheck($requestParams->tokenDetails, $requestParams->client_id, $requestParams->client_secret); - $fields = apply_filters('btcbi_sendPulse_refresh_fields', $fields, $apiEndpoint, $token->access_token); - } + $response['sendPulseField'] = Hooks::apply(Config::withPrefix('sendPulse_refresh_fields'), $fields, $apiEndpoint, $token->access_token); - $response['sendPulseField'] = $fields; + /** + * @deprecated 2.7.8 Use `bit_integrations_sendPulse_refresh_fields` filter instead. + * @since 2.7.8 + */ + $response['sendPulseField'] = Hooks::apply('btcbi_sendPulse_refresh_fields', $response['sendPulseField'], $apiEndpoint, $token->access_token); wp_send_json_success($response); } @@ -112,6 +115,7 @@ public function execute($integrationData, $fieldValues) } if (empty($fieldMap) || empty($tokenDetails) || empty($selectedList)) { + // translators: %s: Placeholder value return new WP_Error('REQ_FIELD_EMPTY', wp_sprintf(__('module, fields are required for %s api', 'bit-integrations'), 'SendPulse')); } diff --git a/backend/Actions/SendinBlue/RecordApiHelper.php b/backend/Actions/SendinBlue/RecordApiHelper.php new file mode 100644 index 000000000..4bc7e3713 --- /dev/null +++ b/backend/Actions/SendinBlue/RecordApiHelper.php @@ -0,0 +1,141 @@ +_defaultHeader['Content-Type'] = 'application/json'; + $this->_defaultHeader['api-key'] = $api_key; + $this->_integrationID = $integId; + } + + /** + * Email template must be activate as double optin, button link = {{ params.DOIur }} + * + * @param mixed $data + * @param mixed $integrationDetails + */ + public function insertRecordDoubleOpt($data, $integrationDetails) + { + $templateId = $integrationDetails->templateId; + $redirectionUrl = $integrationDetails->redirectionUrl; + $data['templateId'] = (int) $templateId; + $data['redirectionUrl'] = $redirectionUrl; + if ($data['listIds']) { + $data['includeListIds'] = $data['listIds']; + unset($data['listIds']); + } + + $data = wp_json_encode($data); + $insertRecordEndpoint = "{$this->_apiEndPoint}/doubleOptinConfirmation"; + + return HttpHelper::post($insertRecordEndpoint, $data, $this->_defaultHeader); + } + + public function insertRecord($data) + { + $insertRecordEndpoint = "{$this->_apiEndPoint}"; + + return HttpHelper::post($insertRecordEndpoint, $data, $this->_defaultHeader); + } + + public function existRecord($email) + { + $insertRecordEndpoint = "{$this->_apiEndPoint}/{$email}"; + + return HttpHelper::get($insertRecordEndpoint, null, $this->_defaultHeader); + } + + public function updateRecord($id, $data) + { + $updateRecordEndpoint = "{$this->_apiEndPoint}/{$id}"; + + return HttpHelper::request($updateRecordEndpoint, 'PUT', $data, $this->_defaultHeader); + } + + public function execute($lists, $defaultDataConf, $fieldValues, $fieldMap, $actions, $integrationDetails) + { + $fieldData = $this->setFiledMapping($fieldMap, $fieldValues); + + $fieldData['listIds'] = array_map('intval', $lists); + + $recordApiResponse = null; + $type = 'insert'; + $existRecord = false; + + if (!empty($actions->double_optin)) { + $recordApiResponse = $this->insertRecordDoubleOpt($fieldData, $integrationDetails); + } + if (empty($recordApiResponse) && !empty($actions->update)) { + $response = $this->existRecord($fieldData['email']); + $existRecord = !empty($response->id); + } + + if (!empty($actions->update) && !empty($existRecord)) { + $type = 'update'; + $recordApiResponse = $this->updateRecord($fieldData['email'], wp_json_encode($fieldData)); + $recordApiResponse = empty($recordApiResponse) ? (object) ['success' => true, 'id' => $fieldData['email']] : $recordApiResponse; + } elseif (empty($recordApiResponse)) { + $recordApiResponse = $this->insertRecord(wp_json_encode($fieldData)); + } + + if ($recordApiResponse && isset($recordApiResponse->code)) { + LogHandler::save($this->_integrationID, ['type' => 'record', 'type_name' => $type], 'error', $recordApiResponse); + } else { + LogHandler::save($this->_integrationID, ['type' => 'record', 'type_name' => $type], 'success', $recordApiResponse); + } + + return $recordApiResponse; + } + + private function setFiledMapping($fieldMap, $fieldValues) + { + $fieldData = []; + $attributes = []; + + foreach ($fieldMap as $fieldKey => $fieldPair) { + $sendinBlueField = $fieldPair->sendinBlueField ?? null; + $formField = $fieldPair->formField ?? null; + $customValue = $fieldPair->customValue ?? null; + + if (empty($sendinBlueField)) { + continue; + } + + if ($sendinBlueField === 'email') { + $fieldData['email'] = ($formField === 'custom' && isset($customValue)) + ? Common::replaceFieldWithValue($customValue, $fieldValues) + : $fieldValues[$formField] ?? null; + + continue; + } + + $attributes[$sendinBlueField] = ($formField === 'custom' && isset($customValue)) + ? Common::replaceFieldWithValue($customValue, $fieldValues) + : ($fieldValues[$formField] ?? null); + } + + $fieldData['attributes'] = (object) $attributes; + + return $fieldData; + } +} diff --git a/backend/Actions/SendinBlue/Routes.php b/backend/Actions/SendinBlue/Routes.php new file mode 100644 index 000000000..66a3c1534 --- /dev/null +++ b/backend/Actions/SendinBlue/Routes.php @@ -0,0 +1,13 @@ +_integrationID = $integId; + } + + public function insertRecord($data, $sendyUrl) + { + $header['Content-Type'] = 'application/x-www-form-urlencoded'; + $insertRecordEndpoint = "{$sendyUrl}/subscribe"; + $data['boolean'] = 'true'; + + return HttpHelper::post($insertRecordEndpoint, $data, $header); + } + + public function generateReqDataFromFieldMap($data, $fieldMap) + { + $dataFinal = []; + + foreach ($fieldMap as $key => $value) { + $triggerValue = $value->formField; + $actionValue = $value->sendyField; + + if ($triggerValue == 'custom' && $actionValue == 'customFieldKey' && !empty($value->customFieldKey)) { + $dataFinal[$value->customFieldKey] = Common::replaceFieldWithValue($value->customValue, $data); + } elseif ($triggerValue == 'custom') { + $dataFinal[$actionValue] = Common::replaceFieldWithValue($value->customValue, $data); + } elseif ($actionValue == 'customFieldKey' && !empty($value->customFieldKey)) { + $dataFinal[$value->customFieldKey] = $data[$triggerValue]; + } elseif (!\is_null($data[$triggerValue])) { + $dataFinal[$actionValue] = $data[$triggerValue]; + } + } + + return $dataFinal; + } + + public function execute($integId, $integrationDetails, $fieldValues, $fieldMap, $apiKey) + { + $fieldData = []; + $finalData = $this->generateReqDataFromFieldMap($fieldValues, $fieldMap); + + $listId = $integrationDetails->list_id; + $sendyUrl = $integrationDetails->sendy_url; + $apiKey = $integrationDetails->api_key; + $finalData['list'] = $listId; + $finalData['boolean'] = true; + $finalData['api_key'] = $apiKey; + + $apiResponse = $this->insertRecord($finalData, $sendyUrl); + + if ($apiResponse) { + LogHandler::save($this->_integrationID, ['type' => 'record', 'type_name' => 'add-subscriber'], 'success', wp_json_encode(__('Subscriber added successfully', 'bit-integrations'))); + } else { + LogHandler::save($this->_integrationID, ['type' => 'subscriber', 'type_name' => 'add-subscriber'], 'error', wp_json_encode(__('Failed to add subscriber', 'bit-integrations'))); + } + + return $apiResponse; + } +} diff --git a/backend/Actions/Sendy/Routes.php b/backend/Actions/Sendy/Routes.php new file mode 100644 index 000000000..615dfb60d --- /dev/null +++ b/backend/Actions/Sendy/Routes.php @@ -0,0 +1,14 @@ +_integrationDetails = $integrationDetails; + $this->_integrationID = $integId; + } + + /** + * Execute the integration + * + * @param array $fieldValues Field values from form + * @param array $fieldMap Field mapping + * @param array $actions Actions to perform + * + * @return array + */ + public function execute($fieldValues, $fieldMap) + { + if (!\defined('SEOPRESS_VERSION')) { + return [ + 'success' => false, + 'message' => __('SEOPress is not installed or activated', 'bit-integrations') + ]; + } + + $fieldData = static::setFieldMap($fieldMap, $fieldValues); + + $mainAction = $this->_integrationDetails->mainAction ?? 'update_post_meta'; + + $defaultResponse = [ + 'success' => false, + // translators: %s: Plugin name + 'message' => wp_sprintf(__('%s plugin is not installed or activate', 'bit-integrations'), 'Bit Integrations') + ]; + + switch ($mainAction) { + case 'update_post_meta': + $response = Hooks::apply(Config::withPrefix('seopress_update_post_meta'), $defaultResponse, $fieldData); + + /** + * @deprecated 2.7.8 Use `bit_integrations_seopress_update_post_meta` filter instead. + * @since 2.7.8 + */ + $response = Hooks::apply('btcbi_seopress_update_post_meta', $response, $fieldData); + + $actionType = 'update_post_meta'; + + break; + + default: + $response = [ + 'success' => false, + 'message' => __('Invalid action', 'bit-integrations') + ]; + $actionType = 'unknown'; + + break; + } + + $responseType = $response['success'] ? 'success' : 'error'; + + LogHandler::save($this->_integrationID, ['type' => 'SEOPress', 'type_name' => $actionType], $responseType, $response); + + return $response; + } + + /** + * Prepare field data from field map + * + * @param array $fieldMap Field mapping + * @param array $fieldValues Field values + * + * @return array + */ + private static function setFieldMap($fieldMap, $fieldValues) + { + $fieldData = []; + + foreach ($fieldMap as $fieldPair) { + if (!isset($fieldPair->seoPressField) || \is_null($fieldPair->seoPressField) || $fieldPair->seoPressField === '') { + continue; + } + + $fieldData[$fieldPair->seoPressField] = ($fieldPair->formField == 'custom' && isset($fieldPair->customValue)) + ? Common::replaceFieldWithValue($fieldPair->customValue, $fieldValues) + : $fieldValues[$fieldPair->formField]; + } + + return $fieldData; + } +} diff --git a/backend/Actions/SeoPress/Routes.php b/backend/Actions/SeoPress/Routes.php new file mode 100644 index 000000000..f67670882 --- /dev/null +++ b/backend/Actions/SeoPress/Routes.php @@ -0,0 +1,10 @@ +_payloadBoundary = wp_generate_password(24); + $this->_defaultHeader['Content-Type'] = 'multipart/form-data; boundary=' . $this->_payloadBoundary; + } + + /** + * Helps to execute upload files api + * + * @param string $apiEndPoint slack API base URL + * @param array $data Data to pass to API + * @param mixed $_accessToken + * + * @return array $uploadResponse slack API response + */ + public function uploadFiles($apiEndPoint, $data, $_accessToken) + { + $uploadFileEndpoint = $apiEndPoint . '/files.upload'; + + if (\is_array($data['file'])) { + $file = $data['file'][0]; + } else { + $file = $data['file']; + } + + if (empty($file)) { + return false; + } + + $data['file'] = new CURLFile($file); + + return HttpHelper::post( + $uploadFileEndpoint, + $data, + [ + 'Content-Type' => 'multipart/form-data', + 'Authorization' => 'Bearer ' . $_accessToken + ] + ); + } +} diff --git a/backend/Actions/Slack/RecordApiHelper.php b/backend/Actions/Slack/RecordApiHelper.php new file mode 100644 index 000000000..9e7ed9be1 --- /dev/null +++ b/backend/Actions/Slack/RecordApiHelper.php @@ -0,0 +1,99 @@ +_defaultHeader['Content-Type'] = 'multipart/form-data'; + $this->_integrationID = $integId; + $this->_apiEndPoint = $apiEndPoint; + $this->_accessToken = $access_token; + } + + public function sendMessages($data) + { + $header = [ + 'Authorization' => 'Bearer ' . $this->_accessToken, + 'Accept' => '*/*', + 'verify' => false + ]; + $insertRecordEndpoint = $this->_apiEndPoint . '/chat.postMessage'; + + return HttpHelper::post($insertRecordEndpoint, $data, $header); + } + + public function execute($integrationDetails, $fieldValues) + { + $msg = Common::replaceFieldWithValue($integrationDetails->body, $fieldValues); + $messagesBody = str_replace(['

', '

'], ' ', $msg); + + if (!empty($integrationDetails->actions->attachments)) { + foreach ($fieldValues as $fieldKey => $fieldValue) { + if ($integrationDetails->actions->attachments == $fieldKey) { + $file = $fieldValue; + } + } + + if (!empty($file)) { + $data = [ + 'channels' => $integrationDetails->channel_id, + 'initial_comment' => $messagesBody, + 'text' => $messagesBody, + 'parse_mode' => $integrationDetails->parse_mode, + 'file' => \is_array($file) ? $file[0] : $file + ]; + + $sendPhotoApiHelper = new FilesApiHelper($this->_accessToken); + $recordApiResponse = $sendPhotoApiHelper->uploadFiles($this->_apiEndPoint, $data, $this->_accessToken); + } else { + $data = [ + 'channel' => $integrationDetails->channel_id, + 'text' => $messagesBody, + 'parse_mode' => $integrationDetails->parse_mode + ]; + $recordApiResponse = $this->sendMessages($data); + } + + $type = 'insert'; + } else { + $data = [ + 'channel' => $integrationDetails->channel_id, + 'text' => $messagesBody, + 'parse_mode' => $integrationDetails->parse_mode + ]; + $recordApiResponse = $this->sendMessages($data); + $type = 'insert'; + } + + $recordApiResponse = \is_string($recordApiResponse) ? json_decode($recordApiResponse) : $recordApiResponse; + + if ($recordApiResponse && $recordApiResponse->ok) { + LogHandler::save($this->_integrationID, ['type' => 'record', 'type_name' => $type], 'success', $recordApiResponse); + } else { + LogHandler::save($this->_integrationID, ['type' => 'record', 'type_name' => $type], 'error', $recordApiResponse); + } + + return $recordApiResponse; + } +} diff --git a/backend/Actions/Slack/Routes.php b/backend/Actions/Slack/Routes.php new file mode 100644 index 000000000..56e7ea290 --- /dev/null +++ b/backend/Actions/Slack/Routes.php @@ -0,0 +1,11 @@ +_integrationDetails = $integrationDetails; + self::$integrationID = $integId; + } + + public function generateReqDataFromFieldMap($data, $fieldMap) + { + $dataFinal = []; + + foreach ($fieldMap as $key => $value) { + $triggerValue = $value->formField; + $actionValue = $value->slicewpFormField; + if ($triggerValue === 'custom') { + $dataFinal[$actionValue] = Common::replaceFieldWithValue($value->customValue, $data); + } elseif (!\is_null($data[$triggerValue])) { + $dataFinal[$actionValue] = $data[$triggerValue]; + } + } + + return $dataFinal; + } + + public function addCommissionToUser($data, $statusId, $typeId) + { + $user_id = get_current_user_id(); + $affiliate_id = $this->slicewp_get_user_affiliate_id($user_id); + if (!$affiliate_id) { + return; + } + + $commission_data = [ + 'affiliate_id' => $affiliate_id, + 'visit_id' => 0, + 'date_created' => gmdate('Y-m-d H:i:s', strtotime($data['commission_date'])), + 'type' => $typeId, + 'status' => $statusId, + 'reference' => $data['reference'], + 'customer_id' => 0, + 'origin' => 'bit-integrations', + 'amount' => slicewp_sanitize_amount($data['amount']), + 'currency' => slicewp_get_setting('active_currency', 'USD') + ]; + + return slicewp_insert_commission($commission_data); + } + + public function slicewp_get_user_affiliate_id($user_id) + { + global $wpdb; + // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Direct query needed for SliceWp affiliates + $affiliate = $wpdb->get_results($wpdb->prepare("SELECT id FROM {$wpdb->prefix}slicewp_affiliates WHERE {$wpdb->prefix}slicewp_affiliates.user_id = %d", $user_id)); + + return $affiliate[0]->id; + } + + public function execute( + $mainAction, + $fieldValues, + $fieldMap, + $integrationDetails + ) { + $fieldData = []; + $response = null; + $finalData = $this->generateReqDataFromFieldMap($fieldValues, $fieldMap); + if ($mainAction === '1') { + $statusId = $integrationDetails->statusId; + $typeId = $integrationDetails->typeId; + $response = $this->addCommissionToUser($finalData, $statusId, $typeId); + if ($response && \gettype($response) === 'integer') { + LogHandler::save(self::$integrationID, wp_json_encode(['type' => 'add commission', 'type_name' => 'add-commission-to-user']), 'success', wp_json_encode($response)); + } else { + LogHandler::save(self::$integrationID, wp_json_encode(['type' => 'add commission', 'type_name' => 'add-commission-to-user']), 'error', wp_json_encode(__('Failed to add commission', 'bit-integrations'))); + } + } + + return $response; + } +} diff --git a/backend/Actions/SliceWp/Routes.php b/backend/Actions/SliceWp/Routes.php new file mode 100644 index 000000000..9445e363f --- /dev/null +++ b/backend/Actions/SliceWp/Routes.php @@ -0,0 +1,10 @@ +_integrationDetails = $integrationDetails; + $this->_integrationID = $integId; + $this->_subDomainName = $this->_integrationDetails->subdomain; + $this->apiUserName = $this->_integrationDetails->api_user_name; + $this->apiUserPassword = $this->_integrationDetails->api_user_password; + $this->_defaultHeader = [ + 'Authorization' => 'Basic ' . base64_encode("{$this->apiUserName}:{$this->apiUserPassword}"), + 'Content-Type' => 'application/json' + ]; + } + + public function addSubscriber($finalData) + { + if (empty($finalData['email'])) { + return ['success' => false, 'message' => __('Required field Email is empty', 'bit-integrations'), 'code' => 400]; + } + + $apiEndpoint = "https://{$this->_subDomainName}.sendsmaily.net/api/contact.php"; + + foreach ($finalData as $key => $value) { + $requestParams[$key] = $value; + } + + $requestParams['is_unsubscribed'] = $this->_integrationDetails->actions->unsubscribe ? 1 : 0; + $this->_requestStoringTypes = $this->isExist($apiEndpoint, $finalData['email']) ? 'updated' : 'created'; + + return HttpHelper::post($apiEndpoint, wp_json_encode($requestParams), $this->_defaultHeader); + } + + public function generateReqDataFromFieldMap($data, $fieldMap) + { + $dataFinal = []; + foreach ($fieldMap as $value) { + $triggerValue = $value->formField; + $actionValue = $value->smailyFormField; + if ($triggerValue === 'custom') { + if ($actionValue === 'customFieldKey') { + $dataFinal[$value->customFieldKey] = $value->customValue; + } else { + $dataFinal[$actionValue] = $value->customValue; + } + } elseif (!\is_null($data[$triggerValue])) { + if ($actionValue === 'customFieldKey') { + $dataFinal[$value->customFieldKey] = $data[$triggerValue]; + } else { + $dataFinal[$actionValue] = $data[$triggerValue]; + } + } + } + + return $dataFinal; + } + + public function execute($fieldValues, $fieldMap) + { + $finalData = $this->generateReqDataFromFieldMap($fieldValues, $fieldMap); + $apiResponse = $this->addSubscriber($finalData); + + if ($apiResponse->code === 101) { + $res = ['message' => 'Subscriber ' . $this->_requestStoringTypes . ' successfully']; + LogHandler::save($this->_integrationID, wp_json_encode(['type' => 'subscriber', 'type_name' => 'Subscriber ' . $this->_requestStoringTypes]), 'success', wp_json_encode($res)); + } else { + LogHandler::save($this->_integrationID, wp_json_encode(['type' => 'subscriber', 'type_name' => 'Adding Subscriber']), 'error', wp_json_encode($apiResponse)); + } + + return $apiResponse; + } + + public function isExist($apiEndpoint, $email) + { + $apiEndpoint = "{$apiEndpoint}?email={$email}"; + $response = HttpHelper::get($apiEndpoint, null, $this->_defaultHeader); + + return isset($response->email) ? true : false; + } +} diff --git a/backend/Actions/Smaily/Routes.php b/backend/Actions/Smaily/Routes.php new file mode 100644 index 000000000..5fbcf3225 --- /dev/null +++ b/backend/Actions/Smaily/Routes.php @@ -0,0 +1,10 @@ +integrationDetails = $integrationDetails; + $this->integrationId = $integId; + $this->workspaceId = $workspaceId; + $this->apiToken = $apiToken; + $this->apiUrl = 'https://app.smartsuite.com/api/v1/'; + $this->defaultHeader = [ + 'ACCOUNT-ID' => $workspaceId, + 'Authorization' => 'Token ' . $apiToken, + 'Content-Type' => 'application/json' + ]; + } + + public function handleFilterResponse($response) + { + if ($response) { + return $response; + } + + // translators: %s: Placeholder value + return (object) ['error' => wp_sprintf(__('%s plugin is not installed or activate', 'bit-integrations'), 'Bit Integrations Pro')]; + } + + public function createSolution($finalData) + { + if (isset($this->integrationDetails->selectedLogoColor) && !empty($this->integrationDetails->selectedLogoColor)) { + $finalData['logo_color'] = $this->integrationDetails->selectedLogoColor; + } + $apiEndpoint = $this->apiUrl . 'solutions/'; + + return HttpHelper::post($apiEndpoint, wp_json_encode($finalData), $this->defaultHeader); + } + + public function createTable($requestParams) + { + $response = Hooks::apply(Config::withPrefix('smartSuite_create_table'), false, $requestParams, $this->workspaceId, $this->apiToken, $this->integrationDetails->selectedSolution); + + /** + * @deprecated 2.7.8 Use `bit_integrations_smartSuite_create_table` filter instead. + * @since 2.7.8 + */ + $response = Hooks::apply('btcbi_smartSuite_create_table', $response, $requestParams, $this->workspaceId, $this->apiToken, $this->integrationDetails->selectedSolution); + + return $this->handleFilterResponse($response); + } + + public function createRecord($requestParams) + { + $response = Hooks::apply(Config::withPrefix('smartSuite_create_record'), false, $requestParams, $this->integrationDetails, $this->workspaceId, $this->apiToken); + + /** + * @deprecated 2.7.8 Use `bit_integrations_smartSuite_create_record` filter instead. + * @since 2.7.8 + */ + $response = Hooks::apply('btcbi_smartSuite_create_record', $response, $requestParams, $this->integrationDetails, $this->workspaceId, $this->apiToken); + + return $this->handleFilterResponse($response); + } + + public function generateReqDataFromFieldMap($data, $fieldMap) + { + $dataFinal = []; + foreach ($fieldMap as $key => $value) { + $triggerValue = $value->formField; + $actionValue = $value->smartSuiteFormField; + if ($triggerValue === 'custom') { + $dataFinal[$actionValue] = Common::replaceFieldWithValue($value->customValue, $data); + } elseif (!\is_null($data[$triggerValue])) { + $dataFinal[$actionValue] = $data[$triggerValue]; + } + } + + return $dataFinal; + } + + public function execute($fieldValues, $fieldMap, $actionName) + { + $finalData = $this->generateReqDataFromFieldMap($fieldValues, $fieldMap); + $this->typeName = 'create'; + switch ($actionName) { + case 'solution': + $this->type = 'solution'; + $apiResponse = $this->createSolution($finalData); + + break; + case 'table': + $this->type = 'table'; + $apiResponse = $this->createTable($finalData); + + break; + default: + $this->type = 'record'; + $apiResponse = $this->createRecord($finalData); + } + + if (!is_wp_error($apiResponse) || isset($apiResponse->id) || isset($apiResponse->title)) { + LogHandler::save($this->integrationId, wp_json_encode(['type' => $this->type, 'type_name' => $this->typeName]), 'success', $this->typeName . ' ' . $this->type . ' successfully'); + } else { + LogHandler::save($this->integrationId, wp_json_encode(['type' => $this->type, 'type_name' => $this->typeName . ' ' . $this->type]), 'error', wp_json_encode($apiResponse)); + } + + return $apiResponse; + } +} diff --git a/backend/Actions/SmartSuite/Routes.php b/backend/Actions/SmartSuite/Routes.php new file mode 100644 index 000000000..94c16791d --- /dev/null +++ b/backend/Actions/SmartSuite/Routes.php @@ -0,0 +1,13 @@ +integrationDetails = $integrationDetails; + $this->integrationId = $integId; + $this->apiUrl = 'https://app.suitedash.com/secure-api'; + $this->defaultHeader = [ + 'accept' => 'application/json', + 'X-Public-ID' => $publicId, + 'X-Secret-Key' => $secretKey + ]; + } + + public function addContact($finalData) + { + $this->type = 'Contact'; + $this->typeName = 'Contact created'; + + if (empty($finalData['first_name'])) { + return ['success' => false, 'message' => __('Required field First Name is empty', 'bit-integrations'), 'code' => 400]; + } + if (empty($finalData['last_name'])) { + return ['success' => false, 'message' => __('Required field Last Name is empty', 'bit-integrations'), 'code' => 400]; + } + if (empty($finalData['email'])) { + return ['success' => false, 'message' => __('Required field Email is empty', 'bit-integrations'), 'code' => 400]; + } + if (!isset($this->integrationDetails->selectedRole) || empty($this->integrationDetails->selectedRole)) { + return ['success' => false, 'message' => __('Required Role is empty', 'bit-integrations'), 'code' => 400]; + } + + $contactData = ['role' => $this->integrationDetails->selectedRole]; + $customField = []; + $addressField = []; + foreach ($finalData as $key => $value) { + if (stripos($key, 'address-') === false && stripos($key, 'custom-') === false) { + $contactData[$key] = $value; + } elseif (stripos($key, 'address-') > -1) { + $addressField[str_replace('address-', '', $key)] = $value; + } elseif (stripos($key, 'custom-') > -1) { + $customField[str_replace('custom-', '', $key)] = $value; + } + } + + if (isset($this->integrationDetails->selectedCompany) && !empty($this->integrationDetails->selectedCompany)) { + $contactData['company'] = (object) [ + 'name' => $this->integrationDetails->selectedCompany, + 'create_company_if_not_exists' => true + ]; + } + if (\count($customField)) { + $contactData['custom_fields'] = (object) $customField; + } + if (\count($addressField)) { + $contactData['address'] = (object) $addressField; + } + + $apiEndpoint = $this->apiUrl . '/contact'; + + return HttpHelper::post($apiEndpoint, wp_json_encode($contactData), $this->defaultHeader); + } + + public function generateReqDataFromFieldMap($data, $fieldMap) + { + $dataFinal = []; + foreach ($fieldMap as $value) { + $triggerValue = $value->formField; + $actionValue = $value->suiteDashFormField; + $dataFinal[$actionValue] = ($triggerValue === 'custom') ? $value->customValue : $data[$triggerValue]; + } + + return $dataFinal; + } + + public function execute($fieldValues, $fieldMap, $actionName) + { + $finalData = $this->generateReqDataFromFieldMap($fieldValues, $fieldMap); + if ($actionName === 'contact') { + $apiResponse = $this->addContact($finalData); + } + + if ($apiResponse->success) { + $res = [$this->typeName . ' successfully']; + LogHandler::save($this->integrationId, wp_json_encode(['type' => $this->type, 'type_name' => $this->typeName]), 'success', wp_json_encode($res)); + } else { + LogHandler::save($this->integrationId, wp_json_encode(['type' => $this->type, 'type_name' => $this->type . ' creating']), 'error', wp_json_encode($apiResponse)); + } + + return $apiResponse; + } +} diff --git a/backend/Actions/SuiteDash/Routes.php b/backend/Actions/SuiteDash/Routes.php new file mode 100644 index 000000000..a0350c9a5 --- /dev/null +++ b/backend/Actions/SuiteDash/Routes.php @@ -0,0 +1,12 @@ +actionName; if (empty($fieldMap) || empty($publicId) || empty($actionName) || empty($secretKey)) { + // translators: %s: Placeholder value return new WP_Error('REQ_FIELD_EMPTY', wp_sprintf(__('module, fields are required for %s api', 'bit-integrations'), 'SuiteDash')); } diff --git a/backend/Actions/SureCart/RecordApiHelper.php b/backend/Actions/SureCart/RecordApiHelper.php new file mode 100644 index 000000000..a81121ae1 --- /dev/null +++ b/backend/Actions/SureCart/RecordApiHelper.php @@ -0,0 +1,84 @@ +_integrationID = $integrationId; + } + + public function generateReqDataFromFieldMap($data, $fieldMap) + { + $dataFinal = []; + + foreach ($fieldMap as $key => $value) { + $triggerValue = $value->formField; + $actionValue = $value->SureCartFormField; + if ($triggerValue === 'custom') { + $dataFinal[$actionValue] = Common::replaceFieldWithValue($value->customValue, $data); + } elseif (!\is_null($data[$triggerValue])) { + $dataFinal[$actionValue] = $data[$triggerValue]; + } + } + + return $dataFinal; + } + + public function createCustomer($finalData, $api_key) + { + $requestData = [ + 'headers' => [ + 'Authorization' => 'Bearer ' . $api_key, + 'User-Agent' => 'bit-integrations', + 'Content-Type' => 'application/json', + ], + 'timeout' => 60, + 'sslverify' => false, + 'data_format' => 'body', + 'body' => wp_json_encode( + [ + 'customer' => [ + 'name' => $finalData['customer_first_name'] . ' ' . $finalData['customer_last_name'], + 'email' => $finalData['customer_email'], + 'phone' => $finalData['customer_phone'], + 'live_mode' => true, + ], + ] + ), + ]; + + $request = wp_remote_post('https://api.surecart.com/v1/customers', $requestData); + $response_code = wp_remote_retrieve_response_code($request); + $response_body = wp_remote_retrieve_body($request); + + return [$response_body, $response_code]; + } + + public function execute( + $api_key, + $fieldValues, + $fieldMap, + $integrationDetails, + $mainAction + ) { + $finalData = $this->generateReqDataFromFieldMap($fieldValues, $fieldMap); + + if ($mainAction == '1') { + $apiResponse = $this->createCustomer($finalData, $api_key); + if ($apiResponse[1] === 200) { + LogHandler::save($this->_integrationID, wp_json_encode(['type' => 'create', 'type_name' => 'create-customer']), 'success', $apiResponse[0]); + } else { + LogHandler::save($this->_integrationID, wp_json_encode(['type' => 'create', 'type_name' => 'create-customer']), 'error', $apiResponse[0]); + } + } + + return $apiResponse; + } +} diff --git a/backend/Actions/SureCart/Routes.php b/backend/Actions/SureCart/Routes.php new file mode 100644 index 000000000..1ba85c02e --- /dev/null +++ b/backend/Actions/SureCart/Routes.php @@ -0,0 +1,10 @@ +_integrationID = $integId; + } + + public function grantAccessToGroup($finalData, $selectedGroup) + { + if (empty($finalData['email']) || empty($selectedGroup)) { + return ['success' => false, 'message' => __('Required field email or group is empty!', 'bit-integrations'), 'code' => 400]; + } + + $userId = self::getUserIdFromEmail($finalData['email']); + + if (!$userId) { + return ['success' => false, 'message' => __('The user does not exist on your site, or the email is invalid!', 'bit-integrations'), 'code' => 400]; + } + + Access::grant($userId, $selectedGroup); + + return ['success' => true]; + } + + public function revokeAccessFromGroup($finalData, $selectedGroup) + { + if (empty($finalData['email']) || empty($selectedGroup)) { + return ['success' => false, 'message' => __('Required field email or group is empty!', 'bit-integrations'), 'code' => 400]; + } + + $userId = self::getUserIdFromEmail($finalData['email']); + + if (!$userId) { + return ['success' => false, 'message' => __('The user does not exist on your site, or the email is invalid!', 'bit-integrations'), 'code' => 400]; + } + + Access::revoke($userId, $selectedGroup); + + return ['success' => true]; + } + + public static function getUserIdFromEmail($email) + { + if (empty($email) || !is_email($email) || !email_exists($email)) { + return false; + } + + $get_user = get_user_by('email', $email); + + return $get_user->ID; + } + + public function generateReqDataFromFieldMap($data, $fieldMap) + { + $dataFinal = []; + foreach ($fieldMap as $value) { + $triggerValue = $value->formField; + $actionValue = $value->sureMembersField; + if ($triggerValue === 'custom') { + $dataFinal[$actionValue] = Common::replaceFieldWithValue($value->customValue, $data); + } elseif (!\is_null($data[$triggerValue])) { + $dataFinal[$actionValue] = $data[$triggerValue]; + } + } + + return $dataFinal; + } + + public function execute($fieldValues, $fieldMap, $selectedTask, $selectedGroup) + { + $finalData = $this->generateReqDataFromFieldMap($fieldValues, $fieldMap); + + $responseMessage = $taskType = ''; + + if ($selectedTask === 'grantAccess') { + $response = $this->grantAccessToGroup($finalData, $selectedGroup); + $responseMessage = 'User added to the access group.'; + $taskType = 'Grant Access'; + } elseif ($selectedTask === 'revokeAccess') { + $response = $this->revokeAccessFromGroup($finalData, $selectedGroup); + $responseMessage = 'User removed from the access group.'; + $taskType = 'Revoke Access'; + } + + if ($response['success']) { + $res = ['message' => $responseMessage]; + LogHandler::save($this->_integrationID, wp_json_encode(['type' => 'Access Group', 'type_name' => $taskType]), 'success', wp_json_encode($res)); + } else { + LogHandler::save($this->_integrationID, wp_json_encode(['type' => 'Access Group', 'type_name' => 'Grant or revoke access']), 'error', wp_json_encode($response)); + } + + return $response; + } +} diff --git a/backend/Actions/SureMembers/Routes.php b/backend/Actions/SureMembers/Routes.php new file mode 100644 index 000000000..e8ca8aadb --- /dev/null +++ b/backend/Actions/SureMembers/Routes.php @@ -0,0 +1,11 @@ +integrationDetails = $integrationDetails; + $this->integrationId = $integId; + $this->apiUrl = 'https://api.systeme.io/api'; + $this->defaultHeader = [ + 'x-api-key' => $apiKey, + 'Content-Type' => 'application/json' + ]; + } + + public function addContact($finalData) + { + $this->type = 'Add People to Contacts'; + $this->typeName = 'Add People to Contacts'; + + if (empty($finalData['email'])) { + return ['success' => false, 'message' => __('Required field Email is empty', 'bit-integrations'), 'code' => 400]; + } + + $apiEndpoint = $this->apiUrl . '/contacts'; + $response = HttpHelper::post($apiEndpoint, wp_json_encode($finalData), $this->defaultHeader); + + if (isset($this->integrationDetails->selectedTag) || !empty($this->integrationDetails->selectedTag)) { + $this->addTag($response->id, $this->integrationDetails->selectedTag); + } else { + return $response; + } + } + + public function addTag($contactId, $tag) + { + if (empty($contactId)) { + return ['success' => false, 'message' => __('Contact is not created', 'bit-integrations'), 'code' => 400]; + } + if (empty($tag)) { + return ['success' => false, 'message' => __('Required field tag is empty', 'bit-integrations'), 'code' => 400]; + } + + $apiEndpoint = $this->apiUrl . '/contacts/' . $contactId . '/tags'; + + $data['tagId'] = (int) $tag; + + return $response = HttpHelper::post($apiEndpoint, wp_json_encode($data), $this->defaultHeader); + } + + public function generateReqDataFromFieldMap($data, $fieldMap) + { + $dataFinal = []; + foreach ($fieldMap as $value) { + $triggerValue = $value->formField; + $actionValue = $value->systemeIOFormField; + if ($actionValue == 'email') { + $dataFinal[$actionValue] = ($triggerValue === 'custom') ? $value->customValue : $data[$triggerValue]; + } else { + $dataFinal['fields'][] = (object) [ + 'slug' => $actionValue, + 'value' => ($triggerValue === 'custom') ? $value->customValue : $data[$triggerValue] + ]; + } + } + + return $dataFinal; + } + + public function execute($fieldValues, $fieldMap, $actionName) + { + $finalData = $this->generateReqDataFromFieldMap($fieldValues, $fieldMap); + $apiResponse = $this->addContact($finalData); + + if (!isset($apiResponse->errors)) { + $res = [$this->typeName . ' successfully']; + LogHandler::save($this->integrationId, wp_json_encode(['type' => $this->type, 'type_name' => $this->typeName]), 'success', wp_json_encode($res)); + } else { + LogHandler::save($this->integrationId, wp_json_encode(['type' => $this->type, 'type_name' => $this->type . ' creating']), 'error', wp_json_encode($apiResponse)); + } + + return $apiResponse; + } +} diff --git a/backend/Actions/SystemeIO/Routes.php b/backend/Actions/SystemeIO/Routes.php new file mode 100644 index 000000000..e0a3897b6 --- /dev/null +++ b/backend/Actions/SystemeIO/Routes.php @@ -0,0 +1,12 @@ +actionName; if (empty($fieldMap) || empty($actionName) || empty($apiKey)) { + // translators: %s: Placeholder value return new WP_Error('REQ_FIELD_EMPTY', wp_sprintf(__('module, fields are required for %s api', 'bit-integrations'), 'SystemeIO')); } diff --git a/backend/Actions/TeamsForWooCommerceMemberships/RecordApiHelper.php b/backend/Actions/TeamsForWooCommerceMemberships/RecordApiHelper.php new file mode 100644 index 000000000..9c39a9247 --- /dev/null +++ b/backend/Actions/TeamsForWooCommerceMemberships/RecordApiHelper.php @@ -0,0 +1,145 @@ +_integrationDetails = $integrationDetails; + $this->_integrationID = $integId; + } + + /** + * Execute the integration + * + * @param array $fieldValues Field values from form + * @param array $fieldMap Field mapping + * @param string $mainAction Action to perform + * + * @return array + */ + public function execute($fieldValues, $fieldMap, $mainAction) + { + if (!\function_exists('wc_memberships_for_teams')) { + return [ + 'success' => false, + 'message' => __('Teams for WooCommerce Memberships is not installed or activated', 'bit-integrations') + ]; + } + + $fieldData = static::generateReqDataFromFieldMap($fieldMap, $fieldValues); + + if (!empty($this->_integrationDetails->selectedTeam)) { + $fieldData['team_id'] = $this->_integrationDetails->selectedTeam; + } + if (!empty($this->_integrationDetails->selectedMemberRole)) { + $fieldData['member_role'] = $this->_integrationDetails->selectedMemberRole; + } + + $defaultResponse = [ + 'success' => false, + // translators: %s: Plugin name + 'message' => wp_sprintf(__('%s plugin is not installed or activate', 'bit-integrations'), 'Bit Integrations Pro') + ]; + + // Route to appropriate action method + switch ($mainAction) { + case 'add_member_to_team': + $response = Hooks::apply(Config::withPrefix('teams_for_wc_memberships_add_member'), $defaultResponse, $fieldData, $this->_integrationDetails); + + /** + * @deprecated 2.7.8 Use `bit_integrations_teams_for_wc_memberships_add_member` filter instead. + * @since 2.7.8 + */ + $response = Hooks::apply('btcbi_teams_for_wc_memberships_add_member', $response, $fieldData, $this->_integrationDetails); + $type = 'team_member'; + $actionType = 'add_member_to_team'; + + break; + + case 'remove_member_from_team': + $response = Hooks::apply(Config::withPrefix('teams_for_wc_memberships_remove_member'), $defaultResponse, $fieldData, $this->_integrationDetails); + + /** + * @deprecated 2.7.8 Use `bit_integrations_teams_for_wc_memberships_remove_member` filter instead. + * @since 2.7.8 + */ + $response = Hooks::apply('btcbi_teams_for_wc_memberships_remove_member', $response, $fieldData, $this->_integrationDetails); + $type = 'team_member'; + $actionType = 'remove_member_from_team'; + + break; + + case 'invite_user_to_team': + $response = Hooks::apply(Config::withPrefix('teams_for_wc_memberships_invite_user'), $defaultResponse, $fieldData, $this->_integrationDetails); + + /** + * @deprecated 2.7.8 Use `bit_integrations_teams_for_wc_memberships_invite_user` filter instead. + * @since 2.7.8 + */ + $response = Hooks::apply('btcbi_teams_for_wc_memberships_invite_user', $response, $fieldData, $this->_integrationDetails); + $type = 'team_invitation'; + $actionType = 'invite_user_to_team'; + + break; + + case 'update_member_role': + $response = Hooks::apply(Config::withPrefix('teams_for_wc_memberships_update_role'), $defaultResponse, $fieldData, $this->_integrationDetails); + + /** + * @deprecated 2.7.8 Use `bit_integrations_teams_for_wc_memberships_update_role` filter instead. + * @since 2.7.8 + */ + $response = Hooks::apply('btcbi_teams_for_wc_memberships_update_role', $response, $fieldData, $this->_integrationDetails); + $type = 'team_member'; + $actionType = 'update_member_role'; + + break; + + default: + $response = [ + 'success' => false, + 'message' => __('Invalid action', 'bit-integrations') + ]; + $type = 'TeamsForWooCommerceMemberships'; + $actionType = 'unknown'; + + break; + } + + $responseType = isset($response['success']) && $response['success'] ? 'success' : 'error'; + LogHandler::save($this->_integrationID, ['type' => $type, 'type_name' => $actionType], $responseType, $response); + + return $response; + } + + private function generateReqDataFromFieldMap($fieldMap, $fieldValues) + { + $dataFinal = []; + foreach ($fieldMap as $item) { + $triggerValue = $item->formField; + $actionValue = $item->teamsForWooCommerceMembershipsField; + + $dataFinal[$actionValue] = $triggerValue === 'custom' && isset($item->customValue) ? Common::replaceFieldWithValue($item->customValue, $fieldValues) : $fieldValues[$triggerValue] ?? ''; + } + + return $dataFinal; + } +} diff --git a/backend/Actions/TeamsForWooCommerceMemberships/Routes.php b/backend/Actions/TeamsForWooCommerceMemberships/Routes.php new file mode 100644 index 000000000..33fae41b4 --- /dev/null +++ b/backend/Actions/TeamsForWooCommerceMemberships/Routes.php @@ -0,0 +1,12 @@ +_payloadBoundary = wp_generate_password(24); + $this->_defaultHeader['Content-Type'] = 'multipart/form-data; boundary=' . $this->_payloadBoundary; + } + + /** + * Helps to execute upload files api + * + * @param string $apiEndPoint Telegram API base URL + * @param array $data Data to pass to API + * + * @return array $uploadResponse Telegram API response + */ + public function uploadFiles($apiEndPoint, $data) + { + $mimeType = mime_content_type("{$data['photo']}"); + $fileType = explode('/', $mimeType); + + switch ($fileType[0]) { + case 'image': + $apiMethod = '/sendPhoto'; + $param = 'photo'; + + break; + + case 'audio': + $apiMethod = '/sendAudio'; + $param = 'audio'; + + break; + case 'video': + $apiMethod = '/sendVideo'; + $param = 'video'; + + break; + + default: + $apiMethod = '/sendDocument'; + $param = 'document'; + + break; + } + $uploadFileEndpoint = $apiEndPoint . $apiMethod; + + $data[$param] = new CURLFile("{$data['photo']}"); + + if ($param != 'photo') { + unset($data['photo']); + } + + return HttpHelper::post( + $uploadFileEndpoint, + $data, + [ + 'Content-Type' => 'multipart/form-data', + ] + ); + } + + public function uploadMultipleFiles($apiEndPoint, $data) + { + $param = 'media'; + $uploadMultipleFileEndpoint = $apiEndPoint . '/sendMediaGroup'; + $postFields = [ + 'chat_id' => $data['chat_id'], + 'caption' => $data['caption'], + ]; + + foreach ($data['media'] as $key => $value) { + $mimeType = mime_content_type("{$value}"); + $fileType = explode('/', $mimeType); + unset($data['media'][$key]); + + if ($fileType[0] == 'image') { + $type = 'photo'; + } elseif ($fileType[0] == 'application' || $fileType[0] == 'text') { + $type = 'document'; + } elseif ($fileType[0] == 'application') { + $type = 'document'; + } else { + $type = empty($fileType[0]) ? 'photo' : $fileType[0]; + } + + $media[] = [ + 'type' => $type, + 'media' => "attach://{$key}.path", + 'caption' => $data['caption'], + 'parse_mode' => 'HTML' + ]; + $nameK = "{$key}.path"; + $postFields[$nameK] = new CURLFile(empty(realpath($value)) ? "{$value}" : realpath($value)); + } + $postFields['media'] = wp_json_encode($media); + + if ($param != 'media') { + unset($data['media']); + } + + return HttpHelper::post( + $uploadMultipleFileEndpoint, + $postFields, + [ + 'Content-Type' => 'multipart/form-data', + ] + ); + } +} diff --git a/backend/Actions/Telegram/RecordApiHelper.php b/backend/Actions/Telegram/RecordApiHelper.php new file mode 100644 index 000000000..a47dc8bd1 --- /dev/null +++ b/backend/Actions/Telegram/RecordApiHelper.php @@ -0,0 +1,162 @@ +_defaultHeader['Content-Type'] = 'multipart/form-data'; + $this->_integrationID = $integId; + $this->_apiEndPoint = $apiEndPoint; + } + + public function sendMessages($data) + { + $insertRecordEndpoint = $this->_apiEndPoint . '/sendMessage'; + + return HttpHelper::get($insertRecordEndpoint, $data, $this->_defaultHeader); + } + + public function execute($integrationDetails, $fieldValues) + { + $msg = Common::replaceFieldWithValue($integrationDetails->body, $fieldValues); + $messagesBody = wp_strip_all_tags(static::htmlToMarkdown($msg)); + + if (!empty($integrationDetails->actions->attachments)) { + foreach ($fieldValues as $fieldKey => $fieldValue) { + if ($integrationDetails->actions->attachments == $fieldKey) { + $file = $fieldValue; + } + } + + $file = self::getFiles($file); + if (!empty($file) && \is_array($file) && \count($file) > 1) { + $data = [ + 'chat_id' => $integrationDetails->chat_id, + 'caption' => $messagesBody, + 'media' => $file + ]; + + $sendPhotoApiHelper = new FilesApiHelper(); + $recordApiResponse = $sendPhotoApiHelper->uploadMultipleFiles($this->_apiEndPoint, $data); + $recordApiResponse = \is_string($recordApiResponse) ? json_decode($recordApiResponse) : $recordApiResponse; + + if ($recordApiResponse && $recordApiResponse->ok) { + $data = [ + 'chat_id' => $integrationDetails->chat_id, + 'text' => $messagesBody, + 'parse_mode' => $integrationDetails->parse_mode + ]; + $recordApiResponse = $this->sendMessages($data); + } + } elseif (!empty($file)) { + $data = [ + 'chat_id' => $integrationDetails->chat_id, + 'caption' => $messagesBody, + 'parse_mode' => $integrationDetails->parse_mode, + 'photo' => \is_array($file) ? $file[0] : $file + ]; + + $sendPhotoApiHelper = new FilesApiHelper(); + $recordApiResponse = $sendPhotoApiHelper->uploadFiles($this->_apiEndPoint, $data); + } else { + $data = [ + 'chat_id' => $integrationDetails->chat_id, + 'text' => $messagesBody, + 'parse_mode' => $integrationDetails->parse_mode + ]; + $recordApiResponse = $this->sendMessages($data); + } + + $type = 'insert'; + } else { + $data = [ + 'chat_id' => $integrationDetails->chat_id, + 'text' => $messagesBody, + 'parse_mode' => 'Markdown', + ]; + + $recordApiResponse = $this->sendMessages($data); + $type = 'insert'; + } + $recordApiResponse = \is_string($recordApiResponse) ? json_decode($recordApiResponse) : $recordApiResponse; + + if (!empty($recordApiResponse) && isset($recordApiResponse->ok) && $recordApiResponse->ok == true) { + LogHandler::save($this->_integrationID, ['type' => 'record', 'type_name' => $type], 'success', $recordApiResponse); + } else { + LogHandler::save($this->_integrationID, ['type' => 'record', 'type_name' => $type], 'error', $recordApiResponse); + } + + return $recordApiResponse; + } + + private static function htmlToMarkdown($html) + { + // Regular expressions for matching HTML elements and attributes + $patterns = [ + '/(.*?)<\/b>/s' => '*$1*', // Bold + '/(.*?)<\/strong>/s' => '*$1*', // Bold + '/(.*?)<\/i>/s' => '_$1_', // Italic + '/