Skip to content

feat(integrations): add MainWP action integration#172

Merged
RishadAlam merged 6 commits into
mainfrom
feat/mainwp
Jun 27, 2026
Merged

feat(integrations): add MainWP action integration#172
RishadAlam merged 6 commits into
mainfrom
feat/mainwp

Conversation

@RishadAlam

Copy link
Copy Markdown
Member

Description

Adds MainWP as a new action integration. Bit Integrations flows can now drive a MainWP Dashboard — syncing child sites and managing posts, plugins, and users across the network. The free plugin ships the full integration UI and backend scaffolding; the eight operations themselves execute via Pro hooks.

Motivation & Context

MainWP is a popular self-hosted dashboard for managing many WordPress sites from one place. Wiring it in as an action lets users trigger cross-site management (sync, post CRUD, plugin toggling, user creation) from any Bit Integrations trigger, closing a long-standing request for network-level automation.

Type of Change

  • 🐛 Bug fix
  • ✨ New feature
  • 💥 Breaking change
  • 📚 Documentation update
  • ⚡ Improvement
  • 🔄 Code refactor

Key Changes

Backend (Actions/MainWP)

  • Added MainWPControllerisExists() guards on the MainWP Dashboard plugin, mainWPAuthorize() validates availability, refreshSites() returns the managed-site list for the UI, and execute() delegates to RecordApiHelper.
  • Added RecordApiHelper — maps trigger fields to action fields, applies post_type/post_status utilities for post actions, and dispatches one of eight operations (sync_site, sync_all_sites, create_post, update_post, delete_post, activate_plugin, deactivate_plugin, create_user) to Pro via Hooks::apply, then logs success/error through LogHandler.
  • Added Routes.phpmain_wp_authorize and refresh_main_wp_sites AJAX endpoints.
  • Updated Core/Util/AllTriggersName.php — registers MainWP for the integration list.

Frontend (AllIntegrations/MainWP)

  • Added step wizard (MainWP.jsx, MainWPAuthorization.jsx, MainWPIntegLayout.jsx, MainWPFieldMap.jsx, EditMainWP.jsx) and shared helpers (MainWPCommonFunc.js, staticData.js) covering auth, site/action selection, post-type & status utilities, and field mapping.
  • Added MainWP logo asset (mainWP.webp).
  • Updated NewInteg.jsx, EditInteg.jsx, IntegInfo.jsx, SelectAction.jsx, and webhookIntegrations.js to surface MainWP in the integration picker and routing.

Checklist

  • Code follows project style guidelines
  • Self-review completed
  • Tests added/updated
  • Documentation updated if needed
  • README updated if needed

Changelog

  • New Actions
  • MainWP: 8 new events added (Pro) — Sync Site, Sync All Sites, Create/Update/Delete Post, Activate/Deactivate Plugin, Create User.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a new integration for MainWP, allowing users to sync sites, manage posts, control plugins, and create users. The feedback highlights several critical and high-severity issues, including a potential fatal error when accessing stdClass objects as arrays in MainWPController.php, a potential fatal error if a WP_Error object is returned and accessed as an array in RecordApiHelper.php, and a non-static method being called statically. Additionally, suggestions were made to add defensive checks for integration details and a .catch() block to prevent the UI from getting stuck in a loading state during authorization.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment on lines +35 to +43
foreach ($websites as $website) {
if (empty($website['id'])) {
continue;
}
$sites[] = [
'value' => (string) $website['id'],
'label' => ($website['name'] ?? 'Site') . ' (' . ($website['url'] ?? '') . ')',
];
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

The MainWP_DB::instance()->get_sites() method returns an array of stdClass objects. Accessing $website directly as an array (e.g., $website['id']) will throw a fatal error: Cannot use object of type stdClass as array. Casting $website to an array before processing prevents this fatal error and ensures compatibility.

            foreach ($websites as $website) {
                $websiteArray = (array) $website;
                if (empty($websiteArray['id'])) {
                    continue;
                }
                $sites[] = [
                    'value' => (string) $websiteArray['id'],
                    'label' => ($websiteArray['name'] ?? 'Site') . ' (' . ($websiteArray['url'] ?? '') . ')',
                ];
            }

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed — get_sites() returns stdClass objects, so the loop now casts each entry before key access:

foreach ($websites as $website) {
    $website = (array) $website;
    if (empty($website['id'])) {
        continue;
    }
    ...
}

break;
}

$responseType = isset($response['success']) && $response['success'] ? 'success' : 'error';

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

If the response from Hooks::apply is a WP_Error object, accessing it as an array (e.g., $response['success']) will trigger a fatal error in PHP 8.0+ because WP_Error does not implement ArrayAccess. Always validate the response using is_wp_error() before accessing it as an array.

        if (is_wp_error($response)) {
            $response = [
                'success' => false,
                'message' => $response->get_error_message(),
            ];
        }

        $responseType = isset($response['success']) && $response['success'] ? 'success' : 'error';
References
  1. In PHP, when handling responses that may return a WP_Error object, always use the is_wp_error() function to validate the response before accessing it as an array or object. This prevents fatal errors in PHP 8.0+.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed — added an is_wp_error() guard before the array access so a WP_Error from Hooks::apply is normalized first:

if (is_wp_error($response)) {
    $response = ['success' => false, 'message' => $response->get_error_message()];
}
$responseType = isset($response['success']) && $response['success'] ? 'success' : 'error';

wp_send_json_success(true);
}

public function refreshSites(): void

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The refreshSites method is registered as a route callback and called statically, but it is declared as a non-static public method. Since it does not access any instance properties ($this), it should be declared as static to adhere to best practices and avoid PHP deprecation warnings or notices.

    public static function refreshSites(): void
References
  1. In PHP, declare methods as static if they do not access any instance properties ($this) and are intended to be called statically.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed — refreshSites() is now declared static (it never touches $this), matching mainWPAuthorize():

public static function refreshSites(): void

Note: Route::action() already resolves static/instance via ReflectionMethod::isStatic(), so this is a consistency/best-practice change with no runtime behavior difference.

Comment on lines +28 to +33
if (!class_exists('\MainWP\Dashboard\MainWP_DB')) {
return [
'success' => false,
'message' => __('MainWP Dashboard is not installed or activated', 'bit-integrations'),
];
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

To ensure defensive programming and prevent potential warnings or fatal errors in PHP 8.0+, we should verify that $this->_integrationDetails is not empty before accessing its properties (like mainAction, selectedSite, or utilities).

        if (!class_exists('\\MainWP\\Dashboard\\MainWP_DB')) {
            return [
                'success' => false,
                'message' => __('MainWP Dashboard is not installed or activated', 'bit-integrations'),
            ];
        }

        if (empty($this->_integrationDetails)) {
            return [
                'success' => false,
                'message' => __('Integration details are missing', 'bit-integrations'),
            ];
        }

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed — added a guard right after the class_exists check:

if (empty($this->_integrationDetails)) {
    return [
        'success' => false,
        'message' => __('Integration details are missing', 'bit-integrations'),
    ];
}

Comment on lines +23 to +33
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)
})

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The bitsFetch promise does not have a .catch() block. If the network request fails or the server returns an error, the promise will reject, and setIsLoading(false) will never be called, leaving the UI stuck in a permanent loading state. Adding a .catch() block ensures the loading state is properly reset.

    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)
      })

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed — added a .catch() so the loading/auth-message state resets on a rejected request:

bitsFetch({}, 'main_wp_authorize')
  .then(result => {
    if (result?.success) { ... }
    setIsLoading(false)
    setShowAuthMsg(true)
  })
  .catch(() => {
    setIsLoading(false)
    setShowAuthMsg(true)
  })

- 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
abdul-kaioum
abdul-kaioum previously approved these changes Jun 27, 2026
@github-actions

Copy link
Copy Markdown

🔍 WordPress Plugin Check Report

⚠️ Status: Passed with warnings

📊 Report

🎯 Total Issues ❌ Errors ⚠️ Warnings
1 0 1

⚠️ Warnings (1)

📁 readme.txt (1 warning)
📍 Line 🔖 Check 💬 Message
0 mismatched_plugin_name Plugin name "Bit integrations - Form Integration, Webhook, Spreadsheets, CRM, LMS & Email Automation" is different from the name declared in plugin header "Bit Integrations".

🤖 Generated by WordPress Plugin Check Action • Learn more about Plugin Check

@RishadAlam RishadAlam merged commit 3e0e169 into main Jun 27, 2026
1 check passed
@RishadAlam RishadAlam deleted the feat/mainwp branch June 27, 2026 06:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants