From 9f5da0dbadede1ed64ba19d0337860d615bd1d4a Mon Sep 17 00:00:00 2001 From: Rishad Alam <101513331+RishadAlam@users.noreply.github.com> Date: Sat, 23 May 2026 17:38:20 +0600 Subject: [PATCH 1/4] feat: add MainWP integration support with necessary components and API handlers --- backend/Actions/MainWP/MainWPController.php | 65 +++++++ backend/Actions/MainWP/RecordApiHelper.php | 123 +++++++++++++ backend/Actions/MainWP/Routes.php | 11 ++ backend/Core/Util/AllTriggersName.php | 1 + .../components/AllIntegrations/EditInteg.jsx | 3 + .../components/AllIntegrations/IntegInfo.jsx | 3 + .../AllIntegrations/MainWP/EditMainWP.jsx | 76 ++++++++ .../AllIntegrations/MainWP/MainWP.jsx | 109 ++++++++++++ .../MainWP/MainWPAuthorization.jsx | 105 +++++++++++ .../MainWP/MainWPCommonFunc.js | 51 ++++++ .../AllIntegrations/MainWP/MainWPFieldMap.jsx | 104 +++++++++++ .../MainWP/MainWPIntegLayout.jsx | 163 ++++++++++++++++++ .../AllIntegrations/MainWP/staticData.js | 52 ++++++ .../components/AllIntegrations/NewInteg.jsx | 10 ++ .../src/components/Flow/New/SelectAction.jsx | 1 + 15 files changed, 877 insertions(+) create mode 100644 backend/Actions/MainWP/MainWPController.php create mode 100644 backend/Actions/MainWP/RecordApiHelper.php create mode 100644 backend/Actions/MainWP/Routes.php create mode 100644 frontend/src/components/AllIntegrations/MainWP/EditMainWP.jsx create mode 100644 frontend/src/components/AllIntegrations/MainWP/MainWP.jsx create mode 100644 frontend/src/components/AllIntegrations/MainWP/MainWPAuthorization.jsx create mode 100644 frontend/src/components/AllIntegrations/MainWP/MainWPCommonFunc.js create mode 100644 frontend/src/components/AllIntegrations/MainWP/MainWPFieldMap.jsx create mode 100644 frontend/src/components/AllIntegrations/MainWP/MainWPIntegLayout.jsx create mode 100644 frontend/src/components/AllIntegrations/MainWP/staticData.js diff --git a/backend/Actions/MainWP/MainWPController.php b/backend/Actions/MainWP/MainWPController.php new file mode 100644 index 000000000..0a58c0231 --- /dev/null +++ b/backend/Actions/MainWP/MainWPController.php @@ -0,0 +1,65 @@ +get_sites(); + + $sites = []; + if (!empty($websites)) { + foreach ($websites as $website) { + if (empty($website->id)) { + continue; + } + $sites[] = [ + 'value' => (string) $website->id, + 'label' => ($website->name ?? 'Site') . ' (' . ($website->url ?? '') . ')', + ]; + } + } + + wp_send_json_success(['sites' => $sites], 200); + } + + public function execute($integrationData, $fieldValues) + { + $integrationDetails = $integrationData->flow_details; + $integId = $integrationData->id; + $fieldMap = $integrationDetails->field_map; + $utilities = isset($integrationDetails->utilities) ? $integrationDetails->utilities : []; + + if (empty($fieldMap)) { + return new WP_Error('field_map_empty', __('Field map is empty', 'bit-integrations')); + } + + return (new RecordApiHelper($integrationDetails, $integId))->execute($fieldValues, $fieldMap, $utilities); + } +} diff --git a/backend/Actions/MainWP/RecordApiHelper.php b/backend/Actions/MainWP/RecordApiHelper.php new file mode 100644 index 000000000..badfe26a4 --- /dev/null +++ b/backend/Actions/MainWP/RecordApiHelper.php @@ -0,0 +1,123 @@ +_integrationDetails = $integrationDetails; + $this->_integrationID = $integId; + } + + public function execute($fieldValues, $fieldMap, $utilities) + { + if (!class_exists('\MainWP\Dashboard\MainWP_DB')) { + return [ + 'success' => false, + 'message' => __('MainWP Dashboard is not installed or activated', 'bit-integrations'), + ]; + } + + $fieldData = static::generateReqDataFromFieldMap($fieldMap, $fieldValues); + $mainAction = $this->_integrationDetails->mainAction ?? 'sync_site'; + + if (!empty($this->_integrationDetails->selectedSite)) { + $fieldData['site_id'] = (int) $this->_integrationDetails->selectedSite; + } + + $defaultResponse = [ + 'success' => false, + 'message' => wp_sprintf(__('%s plugin is not installed or activated', 'bit-integrations'), 'Bit Integrations Pro'), + ]; + + switch ($mainAction) { + case 'sync_site': + $response = Hooks::apply(Config::withPrefix('main_wp_sync_site'), $defaultResponse, $fieldData, $utilities, $this->_integrationDetails); + $actionType = 'sync_site'; + + break; + + case 'sync_all_sites': + $response = Hooks::apply(Config::withPrefix('main_wp_sync_all_sites'), $defaultResponse, $fieldData, $utilities, $this->_integrationDetails); + $actionType = 'sync_all_sites'; + + break; + + case 'create_post': + $response = Hooks::apply(Config::withPrefix('main_wp_create_post'), $defaultResponse, $fieldData, $utilities, $this->_integrationDetails); + $actionType = 'create_post'; + + break; + + case 'update_post': + $response = Hooks::apply(Config::withPrefix('main_wp_update_post'), $defaultResponse, $fieldData, $utilities, $this->_integrationDetails); + $actionType = 'update_post'; + + break; + + case 'delete_post': + $response = Hooks::apply(Config::withPrefix('main_wp_delete_post'), $defaultResponse, $fieldData, $utilities, $this->_integrationDetails); + $actionType = 'delete_post'; + + break; + + case 'activate_plugin': + $response = Hooks::apply(Config::withPrefix('main_wp_activate_plugin'), $defaultResponse, $fieldData, $utilities, $this->_integrationDetails); + $actionType = 'activate_plugin'; + + break; + + case 'deactivate_plugin': + $response = Hooks::apply(Config::withPrefix('main_wp_deactivate_plugin'), $defaultResponse, $fieldData, $utilities, $this->_integrationDetails); + $actionType = 'deactivate_plugin'; + + break; + + case 'create_user': + $response = Hooks::apply(Config::withPrefix('main_wp_create_user'), $defaultResponse, $fieldData, $utilities, $this->_integrationDetails); + $actionType = 'create_user'; + + break; + + default: + $response = ['success' => false, 'message' => __('Invalid action', 'bit-integrations')]; + $actionType = 'unknown'; + + break; + } + + $responseType = isset($response['success']) && $response['success'] ? 'success' : 'error'; + LogHandler::save($this->_integrationID, ['type' => 'MainWP', 'type_name' => $actionType], $responseType, $response); + + return $response; + } + + private static function generateReqDataFromFieldMap($fieldMap, $fieldValues): array + { + $data = []; + foreach ($fieldMap as $item) { + $triggerValue = $item->formField; + $actionValue = $item->mainWPField; + + $data[$actionValue] = $triggerValue === 'custom' && isset($item->customValue) + ? Common::replaceFieldWithValue($item->customValue, $fieldValues) + : $fieldValues[$triggerValue] ?? ''; + } + + return $data; + } +} diff --git a/backend/Actions/MainWP/Routes.php b/backend/Actions/MainWP/Routes.php new file mode 100644 index 000000000..bdead431e --- /dev/null +++ b/backend/Actions/MainWP/Routes.php @@ -0,0 +1,11 @@ + ['name' => 'MailPoet', 'isPro' => true, 'is_active' => false], 'MailMint' => ['name' => 'Mail Mint', 'isPro' => true, 'is_active' => false], 'Mailster' => ['name' => 'Mailster', 'isPro' => true, 'is_active' => false], + 'MainWP' => ['name' => 'MainWP', 'isPro' => true, 'is_active' => false], 'MasterStudyLms' => ['name' => 'MasterStudyLms', 'isPro' => true, 'is_active' => false], 'MasteriyoLMS' => ['name' => 'Masteriyo LMS', 'isPro' => true, 'is_active' => false], 'MyCred' => ['name' => 'myCred', 'isPro' => true, 'is_active' => false], diff --git a/frontend/src/components/AllIntegrations/EditInteg.jsx b/frontend/src/components/AllIntegrations/EditInteg.jsx index 08d20ab3e..6336ff026 100644 --- a/frontend/src/components/AllIntegrations/EditInteg.jsx +++ b/frontend/src/components/AllIntegrations/EditInteg.jsx @@ -159,6 +159,7 @@ const EditNewsletter = lazy(() => import('./Newsletter/EditNewsletter')) const EditSureDash = lazy(() => import('./SureDash/EditSureDash')) const EditSureMembers = lazy(() => import('./SureMembers/EditSureMembers')) const EditMailster = lazy(() => import('./Mailster/EditMailster')) +const EditMainWP = lazy(() => import('./MainWP/EditMainWP')) const EditWPForo = lazy(() => import('./WPForo/EditWPForo')) const EditDokan = lazy(() => import('./Dokan/EditDokan')) const EditJetEngine = lazy(() => import('./JetEngine/EditJetEngine')) @@ -576,6 +577,8 @@ const IntegType = memo(({ allIntegURL, flow }) => { return case 'Mailster': return + case 'MainWP': + return case 'WPForo': return case 'Dokan': diff --git a/frontend/src/components/AllIntegrations/IntegInfo.jsx b/frontend/src/components/AllIntegrations/IntegInfo.jsx index 9c6e28b46..e90631366 100644 --- a/frontend/src/components/AllIntegrations/IntegInfo.jsx +++ b/frontend/src/components/AllIntegrations/IntegInfo.jsx @@ -157,6 +157,7 @@ const NewsletterAuthorization = lazy(() => import('./Newsletter/NewsletterAuthor const SureDashAuthorization = lazy(() => import('./SureDash/SureDashAuthorization')) const SureMembersAuthorization = lazy(() => import('./SureMembers/SureMembersAuthorization')) const MailsterAuthentication = lazy(() => import('./Mailster/MailsterAuthorization')) +const MainWPAuthorization = lazy(() => import('./MainWP/MainWPAuthorization')) const WPForoAuthorization = lazy(() => import('./WPForo/WPForoAuthorization')) const DokanAuthorization = lazy(() => import('./Dokan/DokanAuthorization')) const JetEngineAuthorization = lazy(() => import('./JetEngine/JetEngineAuthorization')) @@ -611,6 +612,8 @@ export default function IntegInfo() { return case 'Mailster': return + case 'MainWP': + return case 'WPForo': return case 'Dokan': diff --git a/frontend/src/components/AllIntegrations/MainWP/EditMainWP.jsx b/frontend/src/components/AllIntegrations/MainWP/EditMainWP.jsx new file mode 100644 index 000000000..dcddc794c --- /dev/null +++ b/frontend/src/components/AllIntegrations/MainWP/EditMainWP.jsx @@ -0,0 +1,76 @@ +import { useState } from 'react' +import { useNavigate, useParams } from 'react-router' +import { useRecoilState, useRecoilValue } from 'recoil' +import { $actionConf, $formFields, $newFlow } from '../../../GlobalStates' +import { __ } from '../../../Utils/i18nwrap' +import SnackMsg from '../../Utilities/SnackMsg' +import { saveActionConf } from '../IntegrationHelpers/IntegrationHelpers' +import IntegrationStepThree from '../IntegrationHelpers/IntegrationStepThree' +import SetEditIntegComponents from '../IntegrationHelpers/SetEditIntegComponents' +import { checkMappedFields, handleInput } from './MainWPCommonFunc' +import MainWPIntegLayout from './MainWPIntegLayout' + +export default function EditMainWP({ allIntegURL }) { + const navigate = useNavigate() + const { id, formID } = useParams() + + const [mainWPConf, setMainWPConf] = useRecoilState($actionConf) + const [flow, setFlow] = useRecoilState($newFlow) + const formFields = useRecoilValue($formFields) + const [isLoading, setIsLoading] = useState(false) + const [snack, setSnackbar] = useState({ show: false }) + + return ( +
+ + +
+ {__('Integration Name:', 'bit-integrations')} + handleInput(e, mainWPConf, setMainWPConf)} + name="name" + value={mainWPConf.name} + type="text" + placeholder={__('Integration Name...', 'bit-integrations')} + /> +
+
+ + + + + + + saveActionConf({ + flow, + setFlow, + allIntegURL, + conf: mainWPConf, + navigate, + id, + edit: 1, + setIsLoading, + setSnackbar + }) + } + disabled={!checkMappedFields(mainWPConf)} + isLoading={isLoading} + dataConf={mainWPConf} + setDataConf={setMainWPConf} + formFields={formFields} + /> +
+
+ ) +} diff --git a/frontend/src/components/AllIntegrations/MainWP/MainWP.jsx b/frontend/src/components/AllIntegrations/MainWP/MainWP.jsx new file mode 100644 index 000000000..cf5182045 --- /dev/null +++ b/frontend/src/components/AllIntegrations/MainWP/MainWP.jsx @@ -0,0 +1,109 @@ +import { useState } from 'react' +import 'react-multiple-select-dropdown-lite/dist/index.css' +import { useNavigate, useParams } from 'react-router' +import BackIcn from '../../../Icons/BackIcn' +import { __ } from '../../../Utils/i18nwrap' +import SnackMsg from '../../Utilities/SnackMsg' +import { saveIntegConfig } from '../IntegrationHelpers/IntegrationHelpers' +import IntegrationStepThree from '../IntegrationHelpers/IntegrationStepThree' +import MainWPAuthorization from './MainWPAuthorization' +import { checkMappedFields } from './MainWPCommonFunc' +import MainWPIntegLayout from './MainWPIntegLayout' + +export default function MainWP({ formFields, setFlow, flow, allIntegURL }) { + const navigate = useNavigate() + const { formID } = useParams() + const [isLoading, setIsLoading] = useState(false) + const [step, setStep] = useState(1) + const [snack, setSnackbar] = useState({ show: false }) + const [mainWPConf, setMainWPConf] = useState({ + name: 'MainWP', + type: 'MainWP', + field_map: [], + actions: {}, + mainAction: '', + mainWPFields: [], + selectedSite: '', + allSites: [] + }) + + const nextPage = val => { + setTimeout(() => { + document.getElementById('btcd-settings-wrp').scrollTop = 0 + }, 300) + + if (val === 3) { + if (!checkMappedFields(mainWPConf)) { + setSnackbar({ + show: true, + msg: __('Please map all required fields to continue.', 'bit-integrations') + }) + return + } + + if (mainWPConf.name !== '' && mainWPConf.field_map.length > 0) { + setStep(val) + } + } else { + setStep(val) + } + } + + return ( +
+ +
+ + {/* STEP 1 */} + + + {/* STEP 2 */} +
+ +
+
+
+ +
+ + {/* STEP 3 */} + + saveIntegConfig(flow, setFlow, allIntegURL, mainWPConf, navigate, '', '', setIsLoading) + } + isLoading={isLoading} + /> +
+ ) +} diff --git a/frontend/src/components/AllIntegrations/MainWP/MainWPAuthorization.jsx b/frontend/src/components/AllIntegrations/MainWP/MainWPAuthorization.jsx new file mode 100644 index 000000000..7142d3954 --- /dev/null +++ b/frontend/src/components/AllIntegrations/MainWP/MainWPAuthorization.jsx @@ -0,0 +1,105 @@ +import { useState } from 'react' +import BackIcn from '../../../Icons/BackIcn' +import bitsFetch from '../../../Utils/bitsFetch' +import { __ } from '../../../Utils/i18nwrap' +import LoaderSm from '../../Loaders/LoaderSm' +import { handleInput } from './MainWPCommonFunc' + +export default function MainWPAuthorization({ + formID, + mainWPConf, + setMainWPConf, + step, + nextPage, + isLoading, + setIsLoading, + setSnackbar +}) { + const [isAuthorized, setIsAuthorized] = useState(false) + const [showAuthMsg, setShowAuthMsg] = useState(false) + + const authorizeHandler = () => { + setIsLoading('auth') + bitsFetch({}, 'main_wp_authorize').then(result => { + if (result?.success) { + setIsAuthorized(true) + setSnackbar({ + show: true, + msg: __('Connected with MainWP Dashboard Successfully', 'bit-integrations') + }) + } + setIsLoading(false) + setShowAuthMsg(true) + }) + } + + return ( +
+
+ {__('Integration Name:', 'bit-integrations')} +
+ handleInput(e, mainWPConf, setMainWPConf)} + name="name" + value={mainWPConf.name} + type="text" + placeholder={__('Integration Name...', 'bit-integrations')} + /> + + {isLoading === 'auth' && ( +
+ + {__('Checking if MainWP Dashboard is active...', 'bit-integrations')} +
+ )} + + {showAuthMsg && !isAuthorized && !isLoading && ( +
+
+
+ +
+
+ {__('MainWP Dashboard is not activated or not installed', 'bit-integrations')} +
+
+
+ )} + + {showAuthMsg && isAuthorized && !isLoading && ( +
+
+ +
+
{__('MainWP Dashboard is activated', 'bit-integrations')}
+
+ )} + + +
+ +
+ ) +} diff --git a/frontend/src/components/AllIntegrations/MainWP/MainWPCommonFunc.js b/frontend/src/components/AllIntegrations/MainWP/MainWPCommonFunc.js new file mode 100644 index 000000000..328c31a9a --- /dev/null +++ b/frontend/src/components/AllIntegrations/MainWP/MainWPCommonFunc.js @@ -0,0 +1,51 @@ +import { create } from 'mutative' +import toast from 'react-hot-toast' +import bitsFetch from '../../../Utils/bitsFetch' +import { __ } from '../../../Utils/i18nwrap' + +export const handleInput = (e, mainWPConf, setMainWPConf) => { + const { name, value } = e.target + + setMainWPConf(prevConf => + create(prevConf, draftConf => { + draftConf[name] = value + }) + ) +} + +export const checkMappedFields = conf => { + if (!conf?.mainAction) return false + + if (conf.mainAction !== 'sync_all_sites' && !conf?.selectedSite) return false + + const requiredFields = conf?.mainWPFields?.filter(f => f.required) || [] + + if (!requiredFields.length) return true + + return requiredFields.every((_, i) => { + const mapItem = conf?.field_map?.[i] + return mapItem?.formField && mapItem?.mainWPField + }) +} + +export const generateMappedField = fields => + fields?.length ? fields.map(f => ({ formField: '', mainWPField: f.key })) : [] + +export const refreshMainWPSites = (setMainWPConf, setIsLoading) => { + setIsLoading('sites') + bitsFetch(null, 'refresh_main_wp_sites') + .then(result => { + if (result?.success && result?.data?.sites) { + setMainWPConf(prevConf => + create(prevConf, draftConf => { + draftConf.allSites = result.data.sites + }) + ) + toast.success(__('Sites fetched successfully', 'bit-integrations')) + } else { + toast.error(__('Failed to fetch sites. Please try again.', 'bit-integrations')) + } + setIsLoading(false) + }) + .catch(() => setIsLoading(false)) +} diff --git a/frontend/src/components/AllIntegrations/MainWP/MainWPFieldMap.jsx b/frontend/src/components/AllIntegrations/MainWP/MainWPFieldMap.jsx new file mode 100644 index 000000000..a442cf492 --- /dev/null +++ b/frontend/src/components/AllIntegrations/MainWP/MainWPFieldMap.jsx @@ -0,0 +1,104 @@ +import { useRecoilValue } from 'recoil' +import { $appConfigState } from '../../../GlobalStates' +import { __, sprintf } from '../../../Utils/i18nwrap' +import { SmartTagField } from '../../../Utils/StaticData/SmartTagField' +import TagifyInput from '../../Utilities/TagifyInput' +import { + addFieldMap, + delFieldMap, + handleCustomValue, + handleFieldMapping +} from '../GlobalIntegrationHelper' + +export default function MainWPFieldMap({ i, formFields, field, mainWPConf, setMainWPConf }) { + const btcbi = useRecoilValue($appConfigState) + const { isPro } = btcbi + + const requiredFlds = mainWPConf?.mainWPFields?.filter(fld => fld.required === true) || [] + const nonRequiredFlds = mainWPConf?.mainWPFields?.filter(fld => fld.required === false) || [] + + return ( +
+
+
+ + + {field.formField === 'custom' && ( + handleCustomValue(e, i, mainWPConf, setMainWPConf)} + label={__('Custom Value', 'bit-integrations')} + className="mr-2" + type="text" + value={field.customValue} + placeholder={__('Custom Value', 'bit-integrations')} + formFields={formFields} + /> + )} + + +
+
+ + {i >= requiredFlds.length && ( + <> + + + + )} +
+ ) +} diff --git a/frontend/src/components/AllIntegrations/MainWP/MainWPIntegLayout.jsx b/frontend/src/components/AllIntegrations/MainWP/MainWPIntegLayout.jsx new file mode 100644 index 000000000..600ab35c4 --- /dev/null +++ b/frontend/src/components/AllIntegrations/MainWP/MainWPIntegLayout.jsx @@ -0,0 +1,163 @@ +import { create } from 'mutative' +import MultiSelect from 'react-multiple-select-dropdown-lite' +import { useRecoilValue } from 'recoil' +import { $appConfigState } from '../../../GlobalStates' +import { __ } from '../../../Utils/i18nwrap' +import { checkIsPro, getProLabel } from '../../Utilities/ProUtilHelpers' +import { addFieldMap } from '../IntegrationHelpers/IntegrationHelpers' +import { + CreatePostFields, + CreateUserFields, + DeletePostFields, + modules, + PluginActionFields, + SyncAllSitesFields, + SyncSiteFields, + UpdatePostFields +} from './staticData' +import { generateMappedField, refreshMainWPSites } from './MainWPCommonFunc' +import MainWPFieldMap from './MainWPFieldMap' + +export default function MainWPIntegLayout({ + formID, + formFields, + mainWPConf, + setMainWPConf, + setSnackbar, + isLoading, + setIsLoading +}) { + const btcbi = useRecoilValue($appConfigState) + const { isPro } = btcbi + + const handleMainAction = value => { + setMainWPConf(prevConf => + create(prevConf, draftConf => { + draftConf.mainAction = value + draftConf.selectedSite = '' + + switch (value) { + case 'sync_site': + draftConf.mainWPFields = SyncSiteFields + break + case 'sync_all_sites': + draftConf.mainWPFields = SyncAllSitesFields + break + case 'create_post': + draftConf.mainWPFields = CreatePostFields + break + case 'update_post': + draftConf.mainWPFields = UpdatePostFields + break + case 'delete_post': + draftConf.mainWPFields = DeletePostFields + break + case 'activate_plugin': + case 'deactivate_plugin': + draftConf.mainWPFields = PluginActionFields + break + case 'create_user': + draftConf.mainWPFields = CreateUserFields + break + default: + draftConf.mainWPFields = [] + } + + draftConf.field_map = generateMappedField(draftConf.mainWPFields) + }) + ) + } + + const needsSite = mainWPConf?.mainAction && mainWPConf.mainAction !== 'sync_all_sites' + + return ( + <> +
+
+ {__('Action:', 'bit-integrations')} + handleMainAction(value)} + options={modules?.map(action => ({ + label: checkIsPro(isPro, action.is_pro) ? action.label : getProLabel(action.label), + value: action.name, + disabled: !checkIsPro(isPro, action.is_pro) + }))} + singleSelect + closeOnSelect + /> +
+ + {needsSite && ( + <> +
+
+ {__('Site:', 'bit-integrations')} + ({ + label: site.label, + value: site.value + })) || [] + } + onChange={val => + setMainWPConf(prevConf => + create(prevConf, draftConf => { + draftConf.selectedSite = val + }) + ) + } + singleSelect + closeOnSelect + /> + +
+ + )} + + {mainWPConf?.mainAction && mainWPConf?.mainWPFields?.length > 0 && ( + <> +
+
+ {__('Field Map', 'bit-integrations')} +
+
+ + {mainWPConf?.field_map?.map((field, i) => ( + + ))} + + {mainWPConf?.mainWPFields?.some(f => !f.required) && ( +
+ +
+ )} + + )} + + ) +} diff --git a/frontend/src/components/AllIntegrations/MainWP/staticData.js b/frontend/src/components/AllIntegrations/MainWP/staticData.js new file mode 100644 index 000000000..8c1fd99b1 --- /dev/null +++ b/frontend/src/components/AllIntegrations/MainWP/staticData.js @@ -0,0 +1,52 @@ +import { __ } from '../../../Utils/i18nwrap' + +export const modules = [ + { name: 'sync_site', label: __('Sync Site', 'bit-integrations'), is_pro: true }, + { name: 'sync_all_sites', label: __('Sync All Sites', 'bit-integrations'), is_pro: true }, + { name: 'create_post', label: __('Create Post', 'bit-integrations'), is_pro: true }, + { name: 'update_post', label: __('Update Post', 'bit-integrations'), is_pro: true }, + { name: 'delete_post', label: __('Delete Post', 'bit-integrations'), is_pro: true }, + { name: 'activate_plugin', label: __('Activate Plugin', 'bit-integrations'), is_pro: true }, + { name: 'deactivate_plugin', label: __('Deactivate Plugin', 'bit-integrations'), is_pro: true }, + { name: 'create_user', label: __('Create User', 'bit-integrations'), is_pro: true } +] + +export const SyncSiteFields = [] + +export const SyncAllSitesFields = [] + +export const CreatePostFields = [ + { key: 'post_type', label: __('Post Type', 'bit-integrations'), required: true }, + { key: 'post_title', label: __('Post Title', 'bit-integrations'), required: true }, + { key: 'post_content', label: __('Post Content', 'bit-integrations'), required: false }, + { key: 'post_status', label: __('Post Status', 'bit-integrations'), required: false }, + { key: 'post_excerpt', label: __('Post Excerpt', 'bit-integrations'), required: false }, + { key: 'post_author', label: __('Post Author ID', 'bit-integrations'), required: false }, + { key: 'post_date', label: __('Post Date', 'bit-integrations'), required: false }, + { key: 'comment_status', label: __('Comment Status', 'bit-integrations'), required: false } +] + +export const UpdatePostFields = [ + { key: 'post_id', label: __('Post ID', 'bit-integrations'), required: true }, + { key: 'post_title', label: __('Post Title', 'bit-integrations'), required: false }, + { key: 'post_content', label: __('Post Content', 'bit-integrations'), required: false }, + { key: 'post_status', label: __('Post Status', 'bit-integrations'), required: false }, + { key: 'post_excerpt', label: __('Post Excerpt', 'bit-integrations'), required: false } +] + +export const DeletePostFields = [ + { key: 'post_id', label: __('Post ID', 'bit-integrations'), required: true } +] + +export const PluginActionFields = [ + { key: 'plugin_slug', label: __('Plugin Slug', 'bit-integrations'), required: true } +] + +export const CreateUserFields = [ + { key: 'user_login', label: __('Username', 'bit-integrations'), required: true }, + { key: 'user_email', label: __('User Email', 'bit-integrations'), required: true }, + { key: 'user_pass', label: __('Password', 'bit-integrations'), required: false }, + { key: 'first_name', label: __('First Name', 'bit-integrations'), required: false }, + { key: 'last_name', label: __('Last Name', 'bit-integrations'), required: false }, + { key: 'role', label: __('User Role', 'bit-integrations'), required: false } +] diff --git a/frontend/src/components/AllIntegrations/NewInteg.jsx b/frontend/src/components/AllIntegrations/NewInteg.jsx index d60b2459b..216f96c4f 100644 --- a/frontend/src/components/AllIntegrations/NewInteg.jsx +++ b/frontend/src/components/AllIntegrations/NewInteg.jsx @@ -158,6 +158,7 @@ const Newsletter = lazy(() => import('./Newsletter/Newsletter')) const SureDash = lazy(() => import('./SureDash/SureDash')) const SureMembers = lazy(() => import('./SureMembers/SureMembers')) const Mailster = lazy(() => import('./Mailster/Mailster')) +const MainWP = lazy(() => import('./MainWP/MainWP')) const WPForo = lazy(() => import('./WPForo/WPForo')) const Dokan = lazy(() => import('./Dokan/Dokan')) const JetEngine = lazy(() => import('./JetEngine/JetEngine')) @@ -1560,6 +1561,15 @@ export default function NewInteg({ allIntegURL }) { setFlow={setFlow} /> ) + case 'MainWP': + return ( + + ) case 'WPForo': return ( Date: Mon, 25 May 2026 11:03:32 +0600 Subject: [PATCH 2/4] feat: update MainWP integration with improved field mapping and new assets --- backend/Actions/MainWP/MainWPController.php | 15 ++++--- backend/Actions/MainWP/RecordApiHelper.php | 6 ++- .../Utils/StaticData/webhookIntegrations.js | 11 +++++- .../AllIntegrations/MainWP/MainWP.jsx | 4 +- .../MainWP/MainWPCommonFunc.js | 10 ++++- .../MainWP/MainWPIntegLayout.jsx | 37 ++++++++++-------- frontend/src/resource/img/integ/mainWP.webp | Bin 0 -> 3920 bytes 7 files changed, 52 insertions(+), 31 deletions(-) create mode 100644 frontend/src/resource/img/integ/mainWP.webp diff --git a/backend/Actions/MainWP/MainWPController.php b/backend/Actions/MainWP/MainWPController.php index 0a58c0231..efb736b1f 100644 --- a/backend/Actions/MainWP/MainWPController.php +++ b/backend/Actions/MainWP/MainWPController.php @@ -3,7 +3,6 @@ namespace BitApps\Integrations\Actions\MainWP; use WP_Error; -use BitApps\Integrations\Actions\MainWP\RecordApiHelper; if (!defined('ABSPATH')) { exit; @@ -32,16 +31,16 @@ public function refreshSites(): void self::isExists(); $websites = \MainWP\Dashboard\MainWP_DB::instance()->get_sites(); - $sites = []; + if (!empty($websites)) { foreach ($websites as $website) { - if (empty($website->id)) { + if (empty($website['id'])) { continue; } $sites[] = [ - 'value' => (string) $website->id, - 'label' => ($website->name ?? 'Site') . ' (' . ($website->url ?? '') . ')', + 'value' => (string) $website['id'], + 'label' => ($website['name'] ?? 'Site') . ' (' . ($website['url'] ?? '') . ')', ]; } } @@ -52,9 +51,9 @@ public function refreshSites(): void public function execute($integrationData, $fieldValues) { $integrationDetails = $integrationData->flow_details; - $integId = $integrationData->id; - $fieldMap = $integrationDetails->field_map; - $utilities = isset($integrationDetails->utilities) ? $integrationDetails->utilities : []; + $integId = $integrationData->id; + $fieldMap = $integrationDetails->field_map; + $utilities = isset($integrationDetails->utilities) ? $integrationDetails->utilities : []; if (empty($fieldMap)) { return new WP_Error('field_map_empty', __('Field map is empty', 'bit-integrations')); diff --git a/backend/Actions/MainWP/RecordApiHelper.php b/backend/Actions/MainWP/RecordApiHelper.php index badfe26a4..5fb7169a0 100644 --- a/backend/Actions/MainWP/RecordApiHelper.php +++ b/backend/Actions/MainWP/RecordApiHelper.php @@ -111,7 +111,11 @@ private static function generateReqDataFromFieldMap($fieldMap, $fieldValues): ar $data = []; foreach ($fieldMap as $item) { $triggerValue = $item->formField; - $actionValue = $item->mainWPField; + $actionValue = $item->mainWPField; + + if (empty($actionValue) || (empty($triggerValue) && $triggerValue !== 'custom')) { + continue; + } $data[$actionValue] = $triggerValue === 'custom' && isset($item->customValue) ? Common::replaceFieldWithValue($item->customValue, $fieldValues) diff --git a/frontend/src/Utils/StaticData/webhookIntegrations.js b/frontend/src/Utils/StaticData/webhookIntegrations.js index 19743f3cc..42eae0def 100644 --- a/frontend/src/Utils/StaticData/webhookIntegrations.js +++ b/frontend/src/Utils/StaticData/webhookIntegrations.js @@ -19,7 +19,6 @@ export const customFormIntegrations = [ 'PopupMaker', 'DiviFormBuilder', 'Bricks', - 'Bricksforge', 'Brizy', 'GutenaForms', 'PieForms', @@ -95,7 +94,15 @@ export const customFormIntegrations = [ 'UserRegistrationMembership', 'UltimateAffiliatePro', 'BookingPress', - "FluentPdfGenerator" + "FluentPdfGenerator", + "B2BKing", + "FormyChat", + "MainWP", + "SureDash", + "WpErp", + "WpDataTables", + "GiveWp", + "SenseiLMS" ] export const actionHookIntegrations = ['ActionHook'] diff --git a/frontend/src/components/AllIntegrations/MainWP/MainWP.jsx b/frontend/src/components/AllIntegrations/MainWP/MainWP.jsx index cf5182045..861aab8cd 100644 --- a/frontend/src/components/AllIntegrations/MainWP/MainWP.jsx +++ b/frontend/src/components/AllIntegrations/MainWP/MainWP.jsx @@ -41,7 +41,7 @@ export default function MainWP({ formFields, setFlow, flow, allIntegURL }) { return } - if (mainWPConf.name !== '' && mainWPConf.field_map.length > 0) { + if (mainWPConf.name !== '') { setStep(val) } } else { @@ -88,7 +88,7 @@ export default function MainWP({ formFields, setFlow, flow, allIntegURL }) {
-
- )} - +
+ +
+
+
+
)} ) diff --git a/frontend/src/resource/img/integ/mainWP.webp b/frontend/src/resource/img/integ/mainWP.webp new file mode 100644 index 0000000000000000000000000000000000000000..52287760a776b1871936ec254cdab9010bc788d6 GIT binary patch literal 3920 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 Date: Mon, 25 May 2026 11:51:33 +0600 Subject: [PATCH 3/4] feat: enhance MainWP integration with utilities support for post type and status --- backend/Actions/MainWP/MainWPController.php | 9 +--- backend/Actions/MainWP/RecordApiHelper.php | 30 +++++++---- .../AllIntegrations/MainWP/MainWP.jsx | 3 +- .../MainWP/MainWPCommonFunc.js | 2 + .../MainWP/MainWPIntegLayout.jsx | 50 +++++++++++++++++++ .../AllIntegrations/MainWP/staticData.js | 15 ++++-- 6 files changed, 88 insertions(+), 21 deletions(-) diff --git a/backend/Actions/MainWP/MainWPController.php b/backend/Actions/MainWP/MainWPController.php index efb736b1f..296da8ae5 100644 --- a/backend/Actions/MainWP/MainWPController.php +++ b/backend/Actions/MainWP/MainWPController.php @@ -2,8 +2,6 @@ namespace BitApps\Integrations\Actions\MainWP; -use WP_Error; - if (!defined('ABSPATH')) { exit; } @@ -53,12 +51,9 @@ public function execute($integrationData, $fieldValues) $integrationDetails = $integrationData->flow_details; $integId = $integrationData->id; $fieldMap = $integrationDetails->field_map; - $utilities = isset($integrationDetails->utilities) ? $integrationDetails->utilities : []; - if (empty($fieldMap)) { - return new WP_Error('field_map_empty', __('Field map is empty', 'bit-integrations')); - } + $RecordApiHelper = new RecordApiHelper($integrationDetails, $integId); - return (new RecordApiHelper($integrationDetails, $integId))->execute($fieldValues, $fieldMap, $utilities); + return $RecordApiHelper->execute($fieldValues, $fieldMap); } } diff --git a/backend/Actions/MainWP/RecordApiHelper.php b/backend/Actions/MainWP/RecordApiHelper.php index 5fb7169a0..75c0326b9 100644 --- a/backend/Actions/MainWP/RecordApiHelper.php +++ b/backend/Actions/MainWP/RecordApiHelper.php @@ -23,7 +23,7 @@ public function __construct($integrationDetails, $integId) $this->_integrationID = $integId; } - public function execute($fieldValues, $fieldMap, $utilities) + public function execute($fieldValues, $fieldMap) { if (!class_exists('\MainWP\Dashboard\MainWP_DB')) { return [ @@ -39,6 +39,16 @@ public function execute($fieldValues, $fieldMap, $utilities) $fieldData['site_id'] = (int) $this->_integrationDetails->selectedSite; } + if (in_array($mainAction, ['create_post', 'update_post'], true)) { + $utils = (array) ($this->_integrationDetails->utilities ?? []); + if (!empty($utils['post_type'])) { + $fieldData['post_type'] = sanitize_text_field($utils['post_type']); + } + if (!empty($utils['post_status'])) { + $fieldData['post_status'] = sanitize_text_field($utils['post_status']); + } + } + $defaultResponse = [ 'success' => false, 'message' => wp_sprintf(__('%s plugin is not installed or activated', 'bit-integrations'), 'Bit Integrations Pro'), @@ -46,49 +56,49 @@ public function execute($fieldValues, $fieldMap, $utilities) switch ($mainAction) { case 'sync_site': - $response = Hooks::apply(Config::withPrefix('main_wp_sync_site'), $defaultResponse, $fieldData, $utilities, $this->_integrationDetails); + $response = Hooks::apply(Config::withPrefix('main_wp_sync_site'), $defaultResponse, $fieldData); $actionType = 'sync_site'; break; case 'sync_all_sites': - $response = Hooks::apply(Config::withPrefix('main_wp_sync_all_sites'), $defaultResponse, $fieldData, $utilities, $this->_integrationDetails); + $response = Hooks::apply(Config::withPrefix('main_wp_sync_all_sites'), $defaultResponse); $actionType = 'sync_all_sites'; break; case 'create_post': - $response = Hooks::apply(Config::withPrefix('main_wp_create_post'), $defaultResponse, $fieldData, $utilities, $this->_integrationDetails); + $response = Hooks::apply(Config::withPrefix('main_wp_create_post'), $defaultResponse, $fieldData); $actionType = 'create_post'; break; case 'update_post': - $response = Hooks::apply(Config::withPrefix('main_wp_update_post'), $defaultResponse, $fieldData, $utilities, $this->_integrationDetails); + $response = Hooks::apply(Config::withPrefix('main_wp_update_post'), $defaultResponse, $fieldData); $actionType = 'update_post'; break; case 'delete_post': - $response = Hooks::apply(Config::withPrefix('main_wp_delete_post'), $defaultResponse, $fieldData, $utilities, $this->_integrationDetails); + $response = Hooks::apply(Config::withPrefix('main_wp_delete_post'), $defaultResponse, $fieldData); $actionType = 'delete_post'; break; case 'activate_plugin': - $response = Hooks::apply(Config::withPrefix('main_wp_activate_plugin'), $defaultResponse, $fieldData, $utilities, $this->_integrationDetails); + $response = Hooks::apply(Config::withPrefix('main_wp_activate_plugin'), $defaultResponse, $fieldData); $actionType = 'activate_plugin'; break; case 'deactivate_plugin': - $response = Hooks::apply(Config::withPrefix('main_wp_deactivate_plugin'), $defaultResponse, $fieldData, $utilities, $this->_integrationDetails); + $response = Hooks::apply(Config::withPrefix('main_wp_deactivate_plugin'), $defaultResponse, $fieldData); $actionType = 'deactivate_plugin'; break; case 'create_user': - $response = Hooks::apply(Config::withPrefix('main_wp_create_user'), $defaultResponse, $fieldData, $utilities, $this->_integrationDetails); + $response = Hooks::apply(Config::withPrefix('main_wp_create_user'), $defaultResponse, $fieldData); $actionType = 'create_user'; break; @@ -111,7 +121,7 @@ private static function generateReqDataFromFieldMap($fieldMap, $fieldValues): ar $data = []; foreach ($fieldMap as $item) { $triggerValue = $item->formField; - $actionValue = $item->mainWPField; + $actionValue = $item->mainWPField; if (empty($actionValue) || (empty($triggerValue) && $triggerValue !== 'custom')) { continue; diff --git a/frontend/src/components/AllIntegrations/MainWP/MainWP.jsx b/frontend/src/components/AllIntegrations/MainWP/MainWP.jsx index 861aab8cd..93830854c 100644 --- a/frontend/src/components/AllIntegrations/MainWP/MainWP.jsx +++ b/frontend/src/components/AllIntegrations/MainWP/MainWP.jsx @@ -24,7 +24,8 @@ export default function MainWP({ formFields, setFlow, flow, allIntegURL }) { mainAction: '', mainWPFields: [], selectedSite: '', - allSites: [] + allSites: [], + utilities: {} }) const nextPage = val => { diff --git a/frontend/src/components/AllIntegrations/MainWP/MainWPCommonFunc.js b/frontend/src/components/AllIntegrations/MainWP/MainWPCommonFunc.js index d7f4d9299..27bd1ae4b 100644 --- a/frontend/src/components/AllIntegrations/MainWP/MainWPCommonFunc.js +++ b/frontend/src/components/AllIntegrations/MainWP/MainWPCommonFunc.js @@ -18,6 +18,8 @@ export const checkMappedFields = conf => { if (conf.mainAction !== 'sync_all_sites' && !conf?.selectedSite) return false + if (conf.mainAction === 'create_post' && !conf?.utilities?.post_type) return false + const requiredFields = conf?.mainWPFields?.filter(f => f.required) || [] if (!requiredFields.length) return true diff --git a/frontend/src/components/AllIntegrations/MainWP/MainWPIntegLayout.jsx b/frontend/src/components/AllIntegrations/MainWP/MainWPIntegLayout.jsx index 12b0b2344..5991b3cce 100644 --- a/frontend/src/components/AllIntegrations/MainWP/MainWPIntegLayout.jsx +++ b/frontend/src/components/AllIntegrations/MainWP/MainWPIntegLayout.jsx @@ -11,6 +11,8 @@ import { DeletePostFields, modules, PluginActionFields, + postStatusOptions, + postTypeOptions, SyncAllSitesFields, SyncSiteFields, UpdatePostFields @@ -30,11 +32,19 @@ export default function MainWPIntegLayout({ const btcbi = useRecoilValue($appConfigState) const { isPro } = btcbi + const setUtility = (key, val) => + setMainWPConf(prev => + create(prev, draft => { + draft.utilities = { ...(draft.utilities || {}), [key]: val } + }) + ) + const handleMainAction = value => { setMainWPConf(prevConf => create(prevConf, draftConf => { draftConf.mainAction = value draftConf.selectedSite = '' + draftConf.utilities = {} switch (value) { case 'sync_site': @@ -127,6 +137,46 @@ export default function MainWPIntegLayout({ )} + {mainWPConf?.mainAction === 'create_post' && ( + <> +
+
+ {__('Post Type:', 'bit-integrations')} + +
+ + )} + + {['create_post', 'update_post'].includes(mainWPConf?.mainAction) && ( + <> +
+
+ {__('Post Status:', 'bit-integrations')} + +
+ + )} + {mainWPConf?.mainAction && mainWPConf?.mainWPFields?.length > 0 && (
{__('Map Fields', 'bit-integrations')} diff --git a/frontend/src/components/AllIntegrations/MainWP/staticData.js b/frontend/src/components/AllIntegrations/MainWP/staticData.js index 8c1fd99b1..0a10b6937 100644 --- a/frontend/src/components/AllIntegrations/MainWP/staticData.js +++ b/frontend/src/components/AllIntegrations/MainWP/staticData.js @@ -15,11 +15,21 @@ export const SyncSiteFields = [] export const SyncAllSitesFields = [] +export const postTypeOptions = [ + { label: __('Post', 'bit-integrations'), value: 'post' }, + { label: __('Page', 'bit-integrations'), value: 'page' } +] + +export const postStatusOptions = [ + { label: __('Publish', 'bit-integrations'), value: 'publish' }, + { label: __('Draft', 'bit-integrations'), value: 'draft' }, + { label: __('Pending', 'bit-integrations'), value: 'pending' }, + { label: __('Private', 'bit-integrations'), value: 'private' } +] + export const CreatePostFields = [ - { key: 'post_type', label: __('Post Type', 'bit-integrations'), required: true }, { key: 'post_title', label: __('Post Title', 'bit-integrations'), required: true }, { key: 'post_content', label: __('Post Content', 'bit-integrations'), required: false }, - { key: 'post_status', label: __('Post Status', 'bit-integrations'), required: false }, { key: 'post_excerpt', label: __('Post Excerpt', 'bit-integrations'), required: false }, { key: 'post_author', label: __('Post Author ID', 'bit-integrations'), required: false }, { key: 'post_date', label: __('Post Date', 'bit-integrations'), required: false }, @@ -30,7 +40,6 @@ export const UpdatePostFields = [ { key: 'post_id', label: __('Post ID', 'bit-integrations'), required: true }, { key: 'post_title', label: __('Post Title', 'bit-integrations'), required: false }, { key: 'post_content', label: __('Post Content', 'bit-integrations'), required: false }, - { key: 'post_status', label: __('Post Status', 'bit-integrations'), required: false }, { key: 'post_excerpt', label: __('Post Excerpt', 'bit-integrations'), required: false } ] From 491e1bf1c031b4ebfa8c76726fdedda97362f094 Mon Sep 17 00:00:00 2001 From: Rishad Alam <101513331+RishadAlam@users.noreply.github.com> Date: Fri, 5 Jun 2026 11:13:06 +0600 Subject: [PATCH 4/4] fix: harden MainWP action against fatal errors from PR review - Cast site entries to array in refreshSites (get_sites returns stdClass) - Make refreshSites static (no $this usage; matches mainWPAuthorize) - Guard empty integrationDetails before accessing properties - Normalize WP_Error responses before array access - Add .catch() to auth bitsFetch so loading state resets on failure --- backend/Actions/MainWP/MainWPController.php | 3 ++- backend/Actions/MainWP/RecordApiHelper.php | 14 ++++++++++ .../MainWP/MainWPAuthorization.jsx | 27 +++++++++++-------- 3 files changed, 32 insertions(+), 12 deletions(-) diff --git a/backend/Actions/MainWP/MainWPController.php b/backend/Actions/MainWP/MainWPController.php index 296da8ae5..066080ecf 100644 --- a/backend/Actions/MainWP/MainWPController.php +++ b/backend/Actions/MainWP/MainWPController.php @@ -24,7 +24,7 @@ public static function mainWPAuthorize(): void wp_send_json_success(true); } - public function refreshSites(): void + public static function refreshSites(): void { self::isExists(); @@ -33,6 +33,7 @@ public function refreshSites(): void if (!empty($websites)) { foreach ($websites as $website) { + $website = (array) $website; if (empty($website['id'])) { continue; } diff --git a/backend/Actions/MainWP/RecordApiHelper.php b/backend/Actions/MainWP/RecordApiHelper.php index 75c0326b9..ca44333e2 100644 --- a/backend/Actions/MainWP/RecordApiHelper.php +++ b/backend/Actions/MainWP/RecordApiHelper.php @@ -32,6 +32,13 @@ public function execute($fieldValues, $fieldMap) ]; } + if (empty($this->_integrationDetails)) { + return [ + 'success' => false, + 'message' => __('Integration details are missing', 'bit-integrations'), + ]; + } + $fieldData = static::generateReqDataFromFieldMap($fieldMap, $fieldValues); $mainAction = $this->_integrationDetails->mainAction ?? 'sync_site'; @@ -110,6 +117,13 @@ public function execute($fieldValues, $fieldMap) break; } + if (is_wp_error($response)) { + $response = [ + 'success' => false, + 'message' => $response->get_error_message(), + ]; + } + $responseType = isset($response['success']) && $response['success'] ? 'success' : 'error'; LogHandler::save($this->_integrationID, ['type' => 'MainWP', 'type_name' => $actionType], $responseType, $response); diff --git a/frontend/src/components/AllIntegrations/MainWP/MainWPAuthorization.jsx b/frontend/src/components/AllIntegrations/MainWP/MainWPAuthorization.jsx index 7142d3954..ef9bb09ff 100644 --- a/frontend/src/components/AllIntegrations/MainWP/MainWPAuthorization.jsx +++ b/frontend/src/components/AllIntegrations/MainWP/MainWPAuthorization.jsx @@ -20,17 +20,22 @@ export default function MainWPAuthorization({ const authorizeHandler = () => { setIsLoading('auth') - bitsFetch({}, 'main_wp_authorize').then(result => { - if (result?.success) { - setIsAuthorized(true) - setSnackbar({ - show: true, - msg: __('Connected with MainWP Dashboard Successfully', 'bit-integrations') - }) - } - setIsLoading(false) - setShowAuthMsg(true) - }) + bitsFetch({}, 'main_wp_authorize') + .then(result => { + if (result?.success) { + setIsAuthorized(true) + setSnackbar({ + show: true, + msg: __('Connected with MainWP Dashboard Successfully', 'bit-integrations') + }) + } + setIsLoading(false) + setShowAuthMsg(true) + }) + .catch(() => { + setIsLoading(false) + setShowAuthMsg(true) + }) } return (