PluginProbe
MainWP Dashboard: Self-hosted WordPress Management for Agencies / 6.2.1
MainWP Dashboard: Self-hosted WordPress Management for Agencies v6.2.1
6.2.2 6.2.1 6.2 6.1.8 6.1.7 6.1.6 6.1.5 6.1.4 6.1.3 6.1.2 6.1.1 6.1 6.0.12 6.0.11 4.6.0.1 5.0 5.0.1 5.0.2 5.0.3 5.0.3.1 5.0.3.2 5.1 5.1.1 5.2 5.2.1 All 155 releases
← All changes | includes/rest-api/controller/version2/class-mainwp-rest-sites-controller.php +337 -26 6.1.6 → 6.2.1 View file →
@@ -369,9 +369,9 @@
369 369 array(
370 370 'methods' => WP_REST_Server::CREATABLE,
371 371 'callback' => array( $this, 'create_item' ),
372 372 'permission_callback' => array( $this, 'get_rest_permissions_check' ),
373 - 'args' => $this->get_endpoint_args_for_item_schema( WP_REST_Server::EDITABLE ),
373 + 'args' => $this->get_add_site_args(),
374 374 ),
375 375 )
376 376 );
377 377
@@ -382,8 +382,9 @@
382 382 array(
383 383 'methods' => WP_REST_Server::EDITABLE,
384 384 'callback' => array( $this, 'update_item' ),
385 385 'permission_callback' => array( $this, 'get_rest_permissions_check' ),
386 + 'args' => $this->get_edit_site_args(),
386 387 ),
387 388 )
388 389 );
389 390
@@ -1765,11 +1766,11 @@
1765 1766
1766 1767 $params = array(
1767 1768 'wpid' => $website->id,
1768 1769 'where_extra' => ' AND dismiss = 0 ',
1769 - 'order' => ! empty( $args['order'] ) ? sanitize_text_field( wp_unslash( $args['order'] ) ) : '',
1770 - 'order_by' => ! empty( $args['orderby'] ) ? sanitize_text_field( wp_unslash( $args['orderby'] ) ) : '',
1771 - 'source' => ! empty( $request['source'] ) ? sanitize_text_field( wp_unslash( $request['source'] ) ) : 'wpadmin',
1770 + 'order' => ! empty( $args['order'] ) ? sanitize_text_field( $args['order'] ) : '',
1771 + 'order_by' => ! empty( $args['orderby'] ) ? sanitize_text_field( $args['orderby'] ) : '',
1772 + 'source' => ! empty( $request['source'] ) ? sanitize_text_field( $request['source'] ) : 'wpadmin',
1772 1773 );
1773 1774
1774 1775 if ( ! empty( $args['paged'] ) && ! empty( $args['items_per_page'] ) ) {
1775 1776 $params['rowcount'] = intval( $args['items_per_page'] );
@@ -1780,10 +1781,10 @@
1780 1781 $params['rowcount'] = $limit;
1781 1782 $params['offset'] = 0;
1782 1783 }
1783 1784
1784 - $params['actions'] = ! empty( $request['actions'] ) ? sanitize_text_field( wp_unslash( $request['actions'] ) ) : '';
1785 - $params['contexts'] = ! empty( $request['contexts'] ) ? sanitize_text_field( wp_unslash( $request['contexts'] ) ) : '';
1785 + $params['actions'] = ! empty( $request['actions'] ) ? sanitize_text_field( $request['actions'] ) : '';
1786 + $params['contexts'] = ! empty( $request['contexts'] ) ? sanitize_text_field( $request['contexts'] ) : '';
1786 1787 $params['total_count'] = ! empty( $request['total_count'] ) ? mainwp_string_to_bool( $request['total_count'] ) : false;
1787 1788 $params['optimize_view'] = ! empty( $request['optimize_view'] ) ? mainwp_string_to_bool( $request['optimize_view'] ) : false;
1788 1789 $params['optimize_with_meta'] = ! empty( $request['optimize_with_meta'] ) ? mainwp_string_to_bool( $request['optimize_with_meta'] ) : false;
1789 1790
@@ -2388,9 +2389,9 @@
2388 2389 'arg_options' => array(
2389 2390 'sanitize_callback' => 'sanitize_text_field',
2390 2391 ),
2391 2392 ),
2392 - 'tags' => array( // response as tags.
2393 + 'tags' => array(
2393 2394 'type' => 'string',
2394 2395 'description' => __( 'Site tags.', 'mainwp' ),
2395 2396 'context' => array( 'view' ),
2396 2397 ),
@@ -2664,9 +2665,9 @@
2664 2665 ),
2665 2666 'force_use_ipv4' => array(
2666 2667 'type' => 'string',
2667 2668 'default' => '',
2668 - 'context' => array( 'view' ),
2669 + 'context' => array( 'view', 'edit' ),
2669 2670 ),
2670 2671 'ssl_version' => array(
2671 2672 'type' => 'string',
2672 2673 'default' => '',
@@ -2681,8 +2682,15 @@
2681 2682 'arg_options' => array(
2682 2683 'sanitize_callback' => 'absint',
2683 2684 ),
2684 2685 ),
2686 + // groupids is validated and sanitized in the database save function
2687 + // before updating the group associations.
2688 + 'groupids' => array(
2689 + 'type' => 'string',
2690 + 'description' => __( 'Site tag/group IDs as a comma-separated string.', 'mainwp' ),
2691 + 'context' => array( 'edit' ),
2692 + ),
2685 2693
2686 2694 ),
2687 2695 );
2688 2696 }
@@ -2688,8 +2696,252 @@
2688 2696 }
2689 2697
2690 2698
2691 2699 /**
2700 + * Arguments registered by the add route.
2701 + *
2702 + * @return array
2703 + */
2704 + protected function get_add_site_args() {
2705 + return array_merge( $this->get_endpoint_args_for_item_schema( WP_REST_Server::EDITABLE ), $this->get_add_site_extra_args() );
2706 + }
2707 +
2708 + /**
2709 + * Get the args a batch create item is validated against.
2710 + *
2711 + * The add route registers the editable schema args plus the add-only extras,
2712 + * so a batch create item has to be held to that same set.
2713 + *
2714 + * @return array
2715 + */
2716 + public function get_batch_create_args() {
2717 + return $this->get_add_site_args();
2718 + }
2719 +
2720 + /**
2721 + * Get the args a batch update item is validated against.
2722 + *
2723 + * @return array
2724 + */
2725 + public function get_batch_update_args() {
2726 + $args = $this->get_edit_site_args();
2727 + $args['id'] = $this->get_batch_item_id_arg();
2728 + return $args;
2729 + }
2730 +
2731 + /**
2732 + * Arguments registered by the edit route.
2733 + *
2734 + * The schema's id arg is the collection filter (wp_parse_id_list) and the edit
2735 + * route identifies the site by its URL segment, so id is dropped here. Schema
2736 + * defaults are dropped too: core injects arg defaults as request params, and
2737 + * prepare_object_for_update() treats every present field as a change, so a
2738 + * name-only edit would otherwise also write suspended = 0 and the update flags.
2739 + *
2740 + * @return array
2741 + */
2742 + protected function get_edit_site_args() {
2743 + $args = $this->get_endpoint_args_for_item_schema( WP_REST_Server::EDITABLE );
2744 + unset( $args['id'] );
2745 + foreach ( $args as &$arg ) {
2746 + unset( $arg['default'] );
2747 + }
2748 + unset( $arg );
2749 + return array_merge( $args, $this->get_edit_site_extra_args() );
2750 + }
2751 +
2752 + /**
2753 + * Extra arguments accepted by the edit route.
2754 + *
2755 + * These are the documented update-only input spellings that prepare_object_for_update()
2756 + * reads. They are absent from the item schema, so core never type-checked them.
2757 + *
2758 + * @return array
2759 + */
2760 + protected function get_edit_site_extra_args() {
2761 + $args = array(
2762 + 'adminname' => array(
2763 + 'description' => __( 'Site administrator username.', 'mainwp' ),
2764 + 'type' => 'string',
2765 + ),
2766 + 'admin' => array(
2767 + 'description' => __( 'Site administrator username, accepted alias of adminname.', 'mainwp' ),
2768 + 'type' => 'string',
2769 + ),
2770 + 'uniqueid' => array(
2771 + 'description' => __( 'Unique security ID.', 'mainwp' ),
2772 + 'type' => 'string',
2773 + ),
2774 + 'groupids' => array(
2775 + 'description' => __( 'Comma-separated tag IDs.', 'mainwp' ),
2776 + 'type' => 'string',
2777 + ),
2778 + 'protocol' => array(
2779 + 'description' => __( 'Site protocol.', 'mainwp' ),
2780 + 'type' => 'string',
2781 + 'enum' => array( 'http', 'https' ),
2782 + ),
2783 + 'sslversion' => array(
2784 + 'description' => __( 'SSL version.', 'mainwp' ),
2785 + 'type' => 'string',
2786 + ),
2787 + 'disablehealthchecking' => array(
2788 + 'description' => __( 'Disable health check.', 'mainwp' ),
2789 + 'type' => 'integer',
2790 + 'enum' => array( 0, 1 ),
2791 + ),
2792 + 'healththreshold' => array(
2793 + 'description' => __( 'Health threshold.', 'mainwp' ),
2794 + 'type' => 'integer',
2795 + ),
2796 + 'backup_before_upgrade' => array(
2797 + 'description' => __( 'Backup before upgrade.', 'mainwp' ),
2798 + 'type' => 'integer',
2799 + 'enum' => array( 0, 1 ),
2800 + ),
2801 + 'ignore_core_updates' => array(
2802 + 'description' => __( 'Ignore WordPress core updates.', 'mainwp' ),
2803 + 'type' => 'integer',
2804 + 'enum' => array( 0, 1 ),
2805 + ),
2806 + 'ignore_plugin_updates' => array(
2807 + 'description' => __( 'Ignore plugin updates.', 'mainwp' ),
2808 + 'type' => 'integer',
2809 + 'enum' => array( 0, 1 ),
2810 + ),
2811 + 'ignore_theme_updates' => array(
2812 + 'description' => __( 'Ignore theme updates.', 'mainwp' ),
2813 + 'type' => 'integer',
2814 + 'enum' => array( 0, 1 ),
2815 + ),
2816 + 'monitoring_emails' => array(
2817 + 'description' => __( 'Monitoring notification email addresses.', 'mainwp' ),
2818 + 'type' => 'string',
2819 + ),
2820 + 'force_use_ipv4' => $this->get_force_use_ipv4_arg(),
2821 + );
2822 +
2823 + // The item schema types the is_ignore* columns as view strings, and the DB layer
2824 + // tests them for truthiness, so left as strings "false" would enable ignoring.
2825 + $aliases = array(
2826 + 'is_ignoreCoreUpdates' => 'ignore_core_updates',
2827 + 'is_ignorePluginUpdates' => 'ignore_plugin_updates',
2828 + 'is_ignoreThemeUpdates' => 'ignore_theme_updates',
2829 + );
2830 + foreach ( $aliases as $alias => $documented ) {
2831 + $args[ $alias ] = array(
2832 + 'description' => $args[ $documented ]['description'],
2833 + 'type' => 'integer',
2834 + 'enum' => array( 0, 1 ),
2835 + );
2836 + }
2837 +
2838 + return $args;
2839 + }
2840 +
2841 + /**
2842 + * Extra arguments accepted by the add route.
2843 + *
2844 + * These are consumed by prepare_object_for_database() but absent from
2845 + * the item schema, so core never type-checked them and an array value
2846 + * reached the string sanitizers and fataled the request. Registering them
2847 + * here types them without touching the published item schema.
2848 + *
2849 + * @return array
2850 + */
2851 + protected function get_add_site_extra_args() {
2852 + return array(
2853 + 'admin' => array(
2854 + 'description' => __( 'Site administrator username.', 'mainwp' ),
2855 + 'type' => 'string',
2856 + ),
2857 + 'adminpassword' => array(
2858 + 'description' => __( 'Site administrator password.', 'mainwp' ),
2859 + 'type' => 'string',
2860 + ),
2861 + 'uniqueid' => array(
2862 + 'description' => __( 'Unique security ID.', 'mainwp' ),
2863 + 'type' => 'string',
2864 + ),
2865 + 'groupids' => array(
2866 + 'description' => __( 'Comma-separated tag IDs.', 'mainwp' ),
2867 + 'type' => 'string',
2868 + ),
2869 + 'ssl_verify' => array(
2870 + 'description' => __( 'Verify the SSL certificate of the child site.', 'mainwp' ),
2871 + 'type' => 'boolean',
2872 + // rest_parse_request_arg validates before it sanitizes, so an explicit
2873 + // JSON null is a 400 instead of being coerced to false past the validator.
2874 + 'sanitize_callback' => 'rest_parse_request_arg',
2875 + 'validate_callback' => 'rest_validate_request_arg',
2876 + ),
2877 + 'force_use_ipv4' => $this->get_force_use_ipv4_arg(),
2878 + );
2879 + }
2880 +
2881 + /**
2882 + * The force_use_ipv4 argument the two input routes accept.
2883 + *
2884 + * The item schema types it as a view string, so a JSON boolean was refused even
2885 + * though OpenAPI documents one. The type order decides how core coerces the value:
2886 + * true / "1" / "0" match boolean first, while 2 and "2" fall through to integer and
2887 + * keep the "use the global setting" value the DB layer stores.
2888 + *
2889 + * @return array
2890 + */
2891 + protected function get_force_use_ipv4_arg() {
2892 + return array(
2893 + 'description' => __( 'Force IPv4: 0 to disable, 1 to force IPv4, 2 to use the global setting.', 'mainwp' ),
2894 + 'type' => array( 'boolean', 'integer', 'string' ),
2895 + // The DB layer only clamps values above 2, so without the enum -1 would be
2896 + // stored as sent and "yes" would silently become 0. Core compares the enum
2897 + // against the coerced value, so "1" and "true" still match true.
2898 + 'enum' => array( false, true, 2 ),
2899 + // Both callbacks re-word an enum rejection: sanitize_params() runs the
2900 + // sanitizer over every parameter bag while has_valid_params() only checks the
2901 + // highest-priority one, so a valid body value alongside an invalid query value
2902 + // is refused by the sanitizer and would otherwise carry core's wording.
2903 + // rest_validate_request_arg() does not call validate_callback itself, so
2904 + // neither closure recurses.
2905 + 'validate_callback' => function ( $value, $request, $param ) {
2906 + return $this->force_use_ipv4_enum_error( rest_validate_request_arg( $value, $request, $param ), $param );
2907 + },
2908 + 'sanitize_callback' => function ( $value, $request, $param ) {
2909 + $valid = $this->force_use_ipv4_enum_error( rest_validate_request_arg( $value, $request, $param ), $param );
2910 + if ( is_wp_error( $valid ) ) {
2911 + return $valid;
2912 + }
2913 +
2914 + return rest_sanitize_request_arg( $value, $request, $param );
2915 + },
2916 + );
2917 + }
2918 +
2919 + /**
2920 + * Re-word a force_use_ipv4 enum rejection, passing anything else through.
2921 + *
2922 + * Core lists the enum with %l, which prints its false member as an empty string
2923 + * ("is not one of , 1, and 2"), so the rejection is worded in the values the route
2924 + * documents instead.
2925 + *
2926 + * @param true|WP_Error $result Result of validating the value.
2927 + * @param string $param Parameter name.
2928 + * @return true|WP_Error
2929 + */
2930 + private function force_use_ipv4_enum_error( $result, $param ) {
2931 + if ( is_wp_error( $result ) && 'rest_not_in_enum' === $result->get_error_code() ) {
2932 + return new WP_Error(
2933 + 'rest_not_in_enum',
2934 + /* translators: %s: parameter name */
2935 + sprintf( __( '%s is not one of 0, 1, or 2.', 'mainwp' ), $param ),
2936 + array( 'status' => 400 )
2937 + );
2938 + }
2939 +
2940 + return $result;
2941 + }
2942 +
2943 + /**
2692 2944 * Prepare a single order for create or update.
2693 2945 *
2694 2946 * @throws MainWP_Rest_Data_Exception When fails to set any item.
2695 2947 * @param WP_REST_Request $request Request object.
@@ -2695,20 +2947,32 @@
2695 2947 * @param WP_REST_Request $request Request object.
2696 2948 * @return array
2697 2949 */
2698 2950 protected function prepare_object_for_database( $request ) {
2699 - $item_fields = array();
2700 - $item_fields['url'] = isset( $request['url'] ) ? sanitize_text_field( wp_unslash( $request['url'] ) ) : '';
2701 - $item_fields['name'] = isset( $request['name'] ) ? sanitize_text_field( wp_unslash( $request['name'] ) ) : '';
2702 - $item_fields['wpadmin'] = isset( $request['admin'] ) ? sanitize_text_field( wp_unslash( $request['admin'] ) ) : '';
2703 - $item_fields['adminpwd'] = isset( $request['adminpassword'] ) ? wp_unslash( $request['adminpassword'] ) : '';
2704 - $item_fields['unique_id'] = isset( $request['uniqueid'] ) ? sanitize_text_field( wp_unslash( $request['uniqueid'] ) ) : '';
2705 - $item_fields['ssl_verify'] = empty( $request['ssl_verify'] ) ? false : intval( $request['ssl_verify'] );
2706 - $item_fields['force_use_ipv4'] = isset( $request['force_use_ipv4'] ) && mainwp_string_to_bool( $request['force_use_ipv4'] ) ? 1 : 0;
2707 - $item_fields['http_user'] = isset( $request['http_user'] ) ? sanitize_text_field( wp_unslash( $request['http_user'] ) ) : '';
2708 - $item_fields['http_pass'] = isset( $request['http_pass'] ) ? wp_unslash( $request['http_pass'] ) : '';
2709 - $item_fields['groupids'] = isset( $request['groupids'] ) && ! empty( $request['groupids'] ) ? explode( ',', sanitize_text_field( wp_unslash( $request['groupids'] ) ) ) : array();
2710 - $item_fields['clientid'] = isset( $request['client_id'] ) && ! empty( $request['client_id'] ) ? intval( $request['client_id'] ) : 0;
2951 + $item_fields = array();
2952 + $item_fields['url'] = isset( $request['url'] ) ? sanitize_text_field( $request['url'] ) : '';
2953 + $item_fields['name'] = isset( $request['name'] ) ? sanitize_text_field( $request['name'] ) : '';
2954 + $item_fields['wpadmin'] = isset( $request['admin'] ) ? sanitize_text_field( $request['admin'] ) : '';
2955 + $item_fields['adminpwd'] = isset( $request['adminpassword'] ) ? (string) $request['adminpassword'] : ''; // Cast only; sanitize_text_field() strips characters a password may legitimately hold.
2956 + $item_fields['unique_id'] = sanitize_text_field( $request['uniqueid'] ?? $request['uniqueId'] ?? '' ); // uniqueid is the documented input spelling, uniqueId is what the item schema registers on this route.
2957 + $item_fields['ssl_verify'] = empty( $request['ssl_verify'] ) ? false : intval( $request['ssl_verify'] );
2958 + // Omitted stays null: fetch_url() then reads the global mainwp_forceUseIPv4 option
2959 + // for the handshake, the same as the UI and the v1 add handler send, and
2960 + // add_website() leaves the column at its default.
2961 + $item_fields['force_use_ipv4'] = null;
2962 + if ( isset( $request['force_use_ipv4'] ) ) {
2963 + // The add route accepts 2 (use the global setting) and mainwp_string_to_bool()
2964 + // would collapse it to 0, so keep it before the boolean conversion.
2965 + if ( 2 === (int) $request['force_use_ipv4'] ) {
2966 + $item_fields['force_use_ipv4'] = 2;
2967 + } else {
2968 + $item_fields['force_use_ipv4'] = mainwp_string_to_bool( $request['force_use_ipv4'] ) ? 1 : 0;
2969 + }
2970 + }
2971 + $item_fields['http_user'] = isset( $request['http_user'] ) ? sanitize_text_field( $request['http_user'] ) : '';
2972 + $item_fields['http_pass'] = isset( $request['http_pass'] ) ? (string) $request['http_pass'] : ''; // Cast only, same reason as adminpwd.
2973 + $item_fields['groupids'] = isset( $request['groupids'] ) && ! empty( $request['groupids'] ) ? explode( ',', sanitize_text_field( $request['groupids'] ) ) : array();
2974 + $item_fields['clientid'] = isset( $request['client_id'] ) && ! empty( $request['client_id'] ) ? intval( $request['client_id'] ) : 0;
2711 2975
2712 2976 /**
2713 2977 * Filters an object before it is inserted via the REST API.
2714 2978 *
@@ -2749,15 +3013,25 @@
2749 3013 'monitoring_emails' => 'monitoring_emails',
2750 3014 );
2751 3015
2752 3016 $data = array();
2753 - foreach ( $map_fields_update as $field ) {
3017 + // The value side is the documented input spelling; the item-schema spelling on
3018 + // the key side is accepted as a fallback, the same way prepare_object_for_database()
3019 + // accepts uniqueId.
3020 + foreach ( $map_fields_update as $schema_field => $field ) {
2754 3021 if ( isset( $request[ $field ] ) ) {
2755 3022 $data[ $field ] = $request[ $field ];
3023 + } elseif ( isset( $request[ $schema_field ] ) ) {
3024 + $data[ $field ] = $request[ $schema_field ];
2756 3025 }
2757 3026 }
2758 3027
2759 - $data['client_id'] = isset( $request['client_id'] ) && ! empty( $request['client_id'] ) ? intval( $request['client_id'] ) : 0;
3028 + // rest_api_update_website() writes client_id whenever the key is set, so an
3029 + // unconditional entry here would unassign the client on every name-only edit.
3030 + // A sent 0 stays the explicit unassign.
3031 + if ( isset( $request['client_id'] ) ) {
3032 + $data['client_id'] = intval( $request['client_id'] );
3033 + }
2760 3034
2761 3035 /**
2762 3036 * Filters an object before it is inserted via the REST API.
2763 3037 *
@@ -3027,12 +3301,22 @@
3027 3301 public function create_item( $request ) { //phpcs:ignore -- NOSONAR - complex.
3028 3302 $resp_data = array();
3029 3303 $resp_data['success'] = 0;
3030 3304 $site_id = 0;
3031 - $url = '';
3032 3305 $data = array(); //phpcs:ignore -- NOSONAR - not used data.
3033 3306 $found_id = 0;
3034 3307 try {
3308 +
3309 + if ( $request->has_param( 'groupids' ) && ! empty( $request['groupids'] ) && false === MainWP_DB_Common::instance()->validate_site_group_ids( $request['groupids'] ) ) {
3310 + return new \WP_Error(
3311 + 'rest_invalid_param',
3312 + __( 'The groupids parameter must be a comma-separated string of positive integers.', 'mainwp' ),
3313 + array(
3314 + 'status' => 400,
3315 + )
3316 + );
3317 + }
3318 +
3035 3319 $item = $this->prepare_object_for_database( $request );
3036 3320
3037 3321 if ( empty( $item['name'] ) && ! empty( $item['url'] ) ) {
3038 3322 $item['name'] = MainWP_Utility::remove_http_prefix( $item['url'], true );
@@ -3038,10 +3322,9 @@
3038 3322 $item['name'] = MainWP_Utility::remove_http_prefix( $item['url'], true );
3039 3323 $item['name'] = rtrim( $item['name'], '/' );
3040 3324 }
3041 3325
3042 - $url = $item['url'];
3043 - $website = MainWP_DB::instance()->get_websites_by_url( $item['url'] );
3326 + $website = MainWP_DB::instance()->get_websites_by_url( $item['url'] );
3044 3327 list( $message, $error, $site_id, $found_id ) = MainWP_Manage_Sites_View::add_wp_site( $website, $item );
3045 3328
3046 3329 if ( ! empty( $site_id ) ) {
3047 3330 $site = MainWP_DB::instance()->get_website_by_id( $site_id );
@@ -3078,13 +3361,41 @@
3078 3361 $website = $this->get_request_item( $request );
3079 3362 if ( is_wp_error( $website ) ) {
3080 3363 return $website;
3081 3364 }
3365 +
3366 + if ( $request->has_param( 'tags' ) ) {
3367 + return new \WP_Error(
3368 + 'rest_invalid_param',
3369 + __( 'The "tags" parameter is not supported for this endpoint.', 'mainwp' ),
3370 + array(
3371 + 'status' => 400,
3372 + )
3373 + );
3374 + }
3375 +
3376 + if ( $request->has_param( 'groupids' ) && ! is_string( $request['groupids'] ) ) {
3377 + return new \WP_Error(
3378 + 'rest_invalid_param',
3379 + __( 'The groupids parameter must be a string.', 'mainwp' ),
3380 + array(
3381 + 'status' => 400,
3382 + )
3383 + );
3384 + }
3385 +
3082 3386 $resp_data = array();
3083 3387 $resp_data['success'] = 0;
3084 3388 try {
3085 3389 $item = $this->prepare_object_for_update( $request );
3086 3390 $result = MainWP_DB_Common::instance()->rest_api_update_website( $website->id, $item );
3391 + if ( is_array( $result ) ) {
3392 + foreach ( array( 'group_updated', 'group_message', 'message' ) as $field ) {
3393 + if ( array_key_exists( $field, $result ) ) {
3394 + $resp_data[ $field ] = $result[ $field ];
3395 + }
3396 + }
3397 + }
3087 3398 if ( is_array( $result ) && ! empty( $result['success'] ) ) {
3088 3399 $resp_data['success'] = 1;
3089 3400 $params = array(
3090 3401 'full_data' => true,
@@ -3094,9 +3405,9 @@
3094 3405 );
3095 3406 $websites = MainWP_DB::instance()->get_websites_for_current_user( $params );
3096 3407 $data = $websites ? current( $websites ) : array();
3097 3408 $resp_data['data'] = $this->prepare_site_item_for_response_context( $data, $request, 'view' );
3098 - } else {
3409 + } elseif ( empty( $resp_data['group_message'] ) && empty( $resp_data['message'] ) ) {
3099 3410 $resp_data['error'] = esc_html__( 'Update site failed. Please try again.', 'mainwp' );
3100 3411 }
3101 3412 } catch ( \Exception $e ) {
3102 3413 $resp_data['error'] = $e->getMessage();