← All changes
|
includes/abilities/class-mainwp-abilities-clients.php
+13
-226
6.3
→
6.2.3
View file →
| @@ -159,12 +159,8 @@ | ||
| 159 | 159 | * @param array $input Validated input parameters. |
| 160 | 160 | * @return array|\WP_Error Result array or error. |
| 161 | 161 | */ |
| 162 | 162 | public static function execute_list_clients( array $input ) { |
| 163 | - // Restricted callers see the clients whose sites overlap their | |
| 164 | - // allowed set instead of a blanket denial. | |
| 165 | - $allowed_sites = MainWP_Abilities_Util::get_allowed_site_ids(); | |
| 166 | - | |
| 167 | 163 | $input = MainWP_Abilities_Util::normalize_input( |
| 168 | 164 | $input, |
| 169 | 165 | array( |
| 170 | 166 | 'page' => 1, |
| @@ -198,26 +194,8 @@ | ||
| 198 | 194 | if ( ! empty( $input['exclude'] ) ) { |
| 199 | 195 | $args['exclude'] = $input['exclude']; |
| 200 | 196 | } |
| 201 | 197 | |
| 202 | - if ( 'all' !== $allowed_sites ) { | |
| 203 | - $accessible = MainWP_Abilities_Util::get_accessible_client_ids( $allowed_sites ); | |
| 204 | - $accessible = empty( $args['include'] ) | |
| 205 | - ? $accessible | |
| 206 | - : array_values( array_intersect( array_map( 'intval', (array) $args['include'] ), $accessible ) ); | |
| 207 | - | |
| 208 | - if ( empty( $accessible ) ) { | |
| 209 | - return array( | |
| 210 | - 'items' => array(), | |
| 211 | - 'page' => (int) $input['page'], | |
| 212 | - 'per_page' => (int) $input['per_page'], | |
| 213 | - 'total' => 0, | |
| 214 | - ); | |
| 215 | - } | |
| 216 | - | |
| 217 | - $args['include'] = $accessible; | |
| 218 | - } | |
| 219 | - | |
| 220 | 198 | $db = MainWP_DB_Client::instance(); |
| 221 | 199 | $clients = $db->get_wp_clients( $args ); |
| 222 | 200 | $total = $db->get_wp_clients( array_merge( $args, array( 'count_only' => true ) ) ); |
| 223 | 201 | |
| @@ -297,22 +275,10 @@ | ||
| 297 | 275 | * @param array $input Validated input parameters (unused but required by ability signature). |
| 298 | 276 | * @return array|\WP_Error Result array or error. |
| 299 | 277 | */ |
| 300 | 278 | public static function execute_count_clients( array $input ) { // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.Found -- NOSONAR Required by ability signature. |
| 301 | - $allowed_sites = MainWP_Abilities_Util::get_allowed_site_ids(); | |
| 279 | + $total = MainWP_DB_Client::instance()->get_wp_clients( array( 'count_only' => true ) ); | |
| 302 | 280 | |
| 303 | - $args = array( 'count_only' => true ); | |
| 304 | - | |
| 305 | - if ( 'all' !== $allowed_sites ) { | |
| 306 | - $accessible = MainWP_Abilities_Util::get_accessible_client_ids( $allowed_sites ); | |
| 307 | - if ( empty( $accessible ) ) { | |
| 308 | - return array( 'total' => 0 ); | |
| 309 | - } | |
| 310 | - $args['include'] = $accessible; | |
| 311 | - } | |
| 312 | - | |
| 313 | - $total = MainWP_DB_Client::instance()->get_wp_clients( $args ); | |
| 314 | - | |
| 315 | 281 | return array( |
| 316 | 282 | 'total' => (int) $total, |
| 317 | 283 | ); |
| 318 | 284 | } |
| @@ -384,14 +350,8 @@ | ||
| 384 | 350 | if ( is_wp_error( $client ) ) { |
| 385 | 351 | return $client; |
| 386 | 352 | } |
| 387 | 353 | |
| 388 | - // Any-site overlap grants read access. | |
| 389 | - $access = MainWP_Abilities_Util::check_client_access( $client, $input ); | |
| 390 | - if ( is_wp_error( $access ) ) { | |
| 391 | - return $access; | |
| 392 | - } | |
| 393 | - | |
| 394 | 354 | return MainWP_Abilities_Util::format_client_for_output( $client ); |
| 395 | 355 | } |
| 396 | 356 | |
| 397 | 357 | /** |
| @@ -408,9 +368,9 @@ | ||
| 408 | 368 | 'category' => 'mainwp-clients', |
| 409 | 369 | 'input_schema' => self::get_add_client_input_schema(), |
| 410 | 370 | 'output_schema' => self::get_client_output_schema(), |
| 411 | 371 | 'execute_callback' => array( self::class, 'execute_add_client' ), |
| 412 | - 'permission_callback' => MainWP_Abilities_Util::make_operation_permission_callback( 'mainwp/add-client-v1', array( MainWP_Abilities_Util::class, 'check_manage_sites_permission' ) ), | |
| 372 | + 'permission_callback' => array( MainWP_Abilities_Util::class, 'check_manage_sites_permission' ), | |
| 413 | 373 | 'meta' => array( |
| 414 | 374 | 'show_in_rest' => true, |
| 415 | 375 | 'annotations' => array( |
| 416 | 376 | 'instructions' => '', |
| @@ -458,44 +418,8 @@ | ||
| 458 | 418 | ); |
| 459 | 419 | } |
| 460 | 420 | |
| 461 | 421 | /** |
| 462 | - * Reject selected_sites entries outside the caller's allowed set. | |
| 463 | - * | |
| 464 | - * A scoped user must not re-home sites they cannot access onto clients | |
| 465 | - * they control, so any out-of-scope ID fails the whole call rather than | |
| 466 | - * being silently dropped. | |
| 467 | - * | |
| 468 | - * @param array $input Ability input, possibly carrying selected_sites. | |
| 469 | - * @return true|\WP_Error True when in scope, WP_Error otherwise. | |
| 470 | - */ | |
| 471 | - public static function check_selected_sites_scope( array $input ) { | |
| 472 | - if ( empty( $input['selected_sites'] ) || ! is_array( $input['selected_sites'] ) ) { | |
| 473 | - return true; | |
| 474 | - } | |
| 475 | - | |
| 476 | - $allowed_sites = MainWP_Abilities_Util::get_allowed_site_ids(); | |
| 477 | - if ( 'all' === $allowed_sites ) { | |
| 478 | - return true; | |
| 479 | - } | |
| 480 | - | |
| 481 | - $selected = array_map( 'intval', $input['selected_sites'] ); | |
| 482 | - $out_of_scope = array_diff( $selected, $allowed_sites ); | |
| 483 | - | |
| 484 | - if ( ! empty( $out_of_scope ) ) { | |
| 485 | - // Only a restricted caller reaches this leg, so the unseen site reads as missing. | |
| 486 | - return new \WP_Error( | |
| 487 | - 'mainwp_site_not_found', | |
| 488 | - /* translators: %d: site ID */ | |
| 489 | - sprintf( __( 'No site found with ID %d.', 'mainwp' ), (int) reset( $out_of_scope ) ), | |
| 490 | - array( 'status' => 404 ) | |
| 491 | - ); | |
| 492 | - } | |
| 493 | - | |
| 494 | - return true; | |
| 495 | - } | |
| 496 | - | |
| 497 | - /** | |
| 498 | 422 | * Execute add-client ability. |
| 499 | 423 | * |
| 500 | 424 | * @param array $input Validated input parameters. |
| 501 | 425 | * @return array|\WP_Error Result array or error. |
| @@ -500,15 +424,8 @@ | ||
| 500 | 424 | * @param array $input Validated input parameters. |
| 501 | 425 | * @return array|\WP_Error Result array or error. |
| 502 | 426 | */ |
| 503 | 427 | public static function execute_add_client( array $input ) { |
| 504 | - // Creation stays open to scoped callers, but they cannot attach | |
| 505 | - // sites outside their allowed set. | |
| 506 | - $scope_check = self::check_selected_sites_scope( $input ); | |
| 507 | - if ( is_wp_error( $scope_check ) ) { | |
| 508 | - return $scope_check; | |
| 509 | - } | |
| 510 | - | |
| 511 | 428 | if ( empty( $input['name'] ) ) { |
| 512 | 429 | return new \WP_Error( |
| 513 | 430 | 'mainwp_invalid_input', |
| 514 | 431 | __( 'Client name is required.', 'mainwp' ), |
| @@ -567,9 +484,9 @@ | ||
| 567 | 484 | 'category' => 'mainwp-clients', |
| 568 | 485 | 'input_schema' => self::get_update_client_input_schema(), |
| 569 | 486 | 'output_schema' => self::get_client_output_schema(), |
| 570 | 487 | 'execute_callback' => array( self::class, 'execute_update_client' ), |
| 571 | - 'permission_callback' => MainWP_Abilities_Util::make_operation_permission_callback( 'mainwp/update-client-v1', array( MainWP_Abilities_Util::class, 'check_manage_sites_permission' ) ), | |
| 488 | + 'permission_callback' => array( MainWP_Abilities_Util::class, 'check_manage_sites_permission' ), | |
| 572 | 489 | 'meta' => array( |
| 573 | 490 | 'show_in_rest' => true, |
| 574 | 491 | 'annotations' => array( |
| 575 | 492 | 'instructions' => '', |
| @@ -630,46 +547,8 @@ | ||
| 630 | 547 | if ( is_wp_error( $client ) ) { |
| 631 | 548 | return $client; |
| 632 | 549 | } |
| 633 | 550 | |
| 634 | - // Any-site overlap grants the update; attaching sites outside the | |
| 635 | - // caller's allowed set is rejected outright. | |
| 636 | - $access = MainWP_Abilities_Util::check_client_access( $client, $input ); | |
| 637 | - if ( is_wp_error( $access ) ) { | |
| 638 | - return $access; | |
| 639 | - } | |
| 640 | - | |
| 641 | - $scope_check = self::check_selected_sites_scope( $input ); | |
| 642 | - if ( is_wp_error( $scope_check ) ) { | |
| 643 | - return $scope_check; | |
| 644 | - } | |
| 645 | - | |
| 646 | - // Preserve memberships the caller cannot see. update_selected_sites_for_client | |
| 647 | - // replaces the client's whole roster, so a restricted caller submitting | |
| 648 | - // selected_sites would silently detach the client's sites outside their allowed | |
| 649 | - // set. Fold those existing out-of-scope sites back in: a partial manager can add | |
| 650 | - // or remove only sites within their own scope, never detach ones they can't see. | |
| 651 | - if ( isset( $input['selected_sites'] ) && is_array( $input['selected_sites'] ) ) { | |
| 652 | - $allowed_sites = MainWP_Abilities_Util::get_allowed_site_ids(); | |
| 653 | - if ( 'all' !== $allowed_sites ) { | |
| 654 | - $current_site_ids = array_map( | |
| 655 | - static fn( $site ) => (int) $site->id, | |
| 656 | - (array) MainWP_DB_Client::instance()->get_websites_by_client_ids( | |
| 657 | - (int) $client->client_id, | |
| 658 | - array( 'no_perm_check' => true ) | |
| 659 | - ) | |
| 660 | - ); | |
| 661 | - $unseen = array_diff( $current_site_ids, $allowed_sites ); | |
| 662 | - if ( ! empty( $unseen ) ) { | |
| 663 | - $input['selected_sites'] = array_values( | |
| 664 | - array_unique( | |
| 665 | - array_merge( array_map( 'intval', $input['selected_sites'] ), $unseen ) | |
| 666 | - ) | |
| 667 | - ); | |
| 668 | - } | |
| 669 | - } | |
| 670 | - } | |
| 671 | - | |
| 672 | 551 | $data = $input; |
| 673 | 552 | $data['client_id'] = $client->client_id; |
| 674 | 553 | |
| 675 | 554 | unset( $data['client_id_or_email'] ); |
| @@ -715,9 +594,9 @@ | ||
| 715 | 594 | 'category' => 'mainwp-clients', |
| 716 | 595 | 'input_schema' => self::get_delete_client_input_schema(), |
| 717 | 596 | 'output_schema' => self::get_delete_client_output_schema(), |
| 718 | 597 | 'execute_callback' => array( self::class, 'execute_delete_client' ), |
| 719 | - 'permission_callback' => MainWP_Abilities_Util::make_operation_permission_callback( 'mainwp/delete-client-v1', array( MainWP_Abilities_Util::class, 'check_manage_sites_permission' ) ), | |
| 598 | + 'permission_callback' => array( MainWP_Abilities_Util::class, 'check_manage_sites_permission' ), | |
| 720 | 599 | 'meta' => array( |
| 721 | 600 | 'show_in_rest' => true, |
| 722 | 601 | 'annotations' => array( |
| 723 | 602 | 'instructions' => 'Destructive operation requires confirm: true. Only call when user explicitly requests deletion.', |
| @@ -806,12 +685,9 @@ | ||
| 806 | 685 | 'dry_run' => false, |
| 807 | 686 | ) |
| 808 | 687 | ); |
| 809 | 688 | |
| 810 | - $dry_run = MainWP_Abilities_Util::get_boolean_input( $input, 'dry_run' ); | |
| 811 | - $confirm = MainWP_Abilities_Util::get_boolean_input( $input, 'confirm' ); | |
| 812 | - | |
| 813 | - if ( $dry_run && $confirm ) { | |
| 689 | + if ( $input['dry_run'] && $input['confirm'] ) { | |
| 814 | 690 | return new \WP_Error( |
| 815 | 691 | 'mainwp_invalid_input', |
| 816 | 692 | __( 'Cannot specify both dry_run and confirm. Use dry_run for preview, confirm for actual deletion.', 'mainwp' ), |
| 817 | 693 | array( 'status' => 400 ) |
| @@ -823,17 +699,9 @@ | ||
| 823 | 699 | if ( is_wp_error( $client ) ) { |
| 824 | 700 | return $client; |
| 825 | 701 | } |
| 826 | 702 | |
| 827 | - // Deleting removes the client for every site it covers, so the | |
| 828 | - // caller's allowed set must cover all of them. Any-site overlap is | |
| 829 | - // read access only. | |
| 830 | - $access = MainWP_Abilities_Util::check_client_access( $client, $input, true ); | |
| 831 | - if ( is_wp_error( $access ) ) { | |
| 832 | - return $access; | |
| 833 | - } | |
| 834 | - | |
| 835 | - if ( $dry_run ) { | |
| 703 | + if ( $input['dry_run'] ) { | |
| 836 | 704 | $sites = MainWP_DB_Client::instance()->get_websites_by_client_ids( $client->client_id ); |
| 837 | 705 | $sites_count = is_array( $sites ) ? count( $sites ) : 0; |
| 838 | 706 | |
| 839 | 707 | $warnings = array(); |
| @@ -860,9 +728,9 @@ | ||
| 860 | 728 | 'warnings' => $warnings, |
| 861 | 729 | ); |
| 862 | 730 | } |
| 863 | 731 | |
| 864 | - if ( ! $confirm ) { | |
| 732 | + if ( ! $input['confirm'] ) { | |
| 865 | 733 | return new \WP_Error( |
| 866 | 734 | 'mainwp_confirmation_required', |
| 867 | 735 | __( 'This is a destructive operation. Set confirm: true or use dry_run: true for preview.', 'mainwp' ), |
| 868 | 736 | array( 'status' => 400 ) |
| @@ -952,29 +820,13 @@ | ||
| 952 | 820 | if ( is_wp_error( $client ) ) { |
| 953 | 821 | return $client; |
| 954 | 822 | } |
| 955 | 823 | |
| 956 | - $access = MainWP_Abilities_Util::check_client_access( $client, $input ); | |
| 957 | - if ( is_wp_error( $access ) ) { | |
| 958 | - return $access; | |
| 959 | - } | |
| 824 | + $sites = MainWP_DB_Client::instance()->get_websites_by_client_ids( $client->client_id ); | |
| 960 | 825 | |
| 961 | - $allowed_sites = MainWP_Abilities_Util::get_allowed_site_ids(); | |
| 962 | - $sites = MainWP_DB_Client::instance()->get_websites_by_client_ids( | |
| 963 | - $client->client_id, | |
| 964 | - array( | |
| 965 | - 'no_perm_check' => true, | |
| 966 | - 'where' => 'wp.is_staging = 0', | |
| 967 | - ) | |
| 968 | - ); | |
| 969 | - | |
| 970 | 826 | $items = array(); |
| 971 | 827 | if ( is_array( $sites ) ) { |
| 972 | 828 | foreach ( $sites as $site ) { |
| 973 | - // A restricted caller sees only their slice of the client. | |
| 974 | - if ( 'all' !== $allowed_sites && ! in_array( (int) $site->id, $allowed_sites, true ) ) { | |
| 975 | - continue; | |
| 976 | - } | |
| 977 | 829 | $items[] = MainWP_Abilities_Util::format_site_for_output( $site ); |
| 978 | 830 | } |
| 979 | 831 | } |
| 980 | 832 | |
| @@ -1054,32 +906,11 @@ | ||
| 1054 | 906 | if ( is_wp_error( $client ) ) { |
| 1055 | 907 | return $client; |
| 1056 | 908 | } |
| 1057 | 909 | |
| 1058 | - $access = MainWP_Abilities_Util::check_client_access( $client, $input ); | |
| 1059 | - if ( is_wp_error( $access ) ) { | |
| 1060 | - return $access; | |
| 1061 | - } | |
| 910 | + $sites = MainWP_DB_Client::instance()->get_websites_by_client_ids( $client->client_id ); | |
| 911 | + $count = is_array( $sites ) ? count( $sites ) : 0; | |
| 1062 | 912 | |
| 1063 | - $allowed_sites = MainWP_Abilities_Util::get_allowed_site_ids(); | |
| 1064 | - $sites = MainWP_DB_Client::instance()->get_websites_by_client_ids( | |
| 1065 | - $client->client_id, | |
| 1066 | - array( | |
| 1067 | - 'no_perm_check' => true, | |
| 1068 | - 'where' => 'wp.is_staging = 0', | |
| 1069 | - ) | |
| 1070 | - ); | |
| 1071 | - | |
| 1072 | - $count = 0; | |
| 1073 | - if ( is_array( $sites ) ) { | |
| 1074 | - foreach ( $sites as $site ) { | |
| 1075 | - if ( 'all' !== $allowed_sites && ! in_array( (int) $site->id, $allowed_sites, true ) ) { | |
| 1076 | - continue; | |
| 1077 | - } | |
| 1078 | - ++$count; | |
| 1079 | - } | |
| 1080 | - } | |
| 1081 | - | |
| 1082 | 913 | return array( |
| 1083 | 914 | 'total' => $count, |
| 1084 | 915 | ); |
| 1085 | 916 | } |
| @@ -1181,13 +1012,8 @@ | ||
| 1181 | 1012 | if ( is_wp_error( $client ) ) { |
| 1182 | 1013 | return $client; |
| 1183 | 1014 | } |
| 1184 | 1015 | |
| 1185 | - $access = MainWP_Abilities_Util::check_client_access( $client, $input ); | |
| 1186 | - if ( is_wp_error( $access ) ) { | |
| 1187 | - return $access; | |
| 1188 | - } | |
| 1189 | - | |
| 1190 | 1016 | if ( ! class_exists( 'MainWP\Dashboard\Module\CostTracker\Cost_Tracker_DB' ) ) { |
| 1191 | 1017 | return new \WP_Error( |
| 1192 | 1018 | 'mainwp_internal_error', |
| 1193 | 1019 | __( 'Cost Tracker database class is not available.', 'mainwp' ), |
| @@ -1195,38 +1021,13 @@ | ||
| 1195 | 1021 | ); |
| 1196 | 1022 | } |
| 1197 | 1023 | |
| 1198 | 1024 | $cost_db = \MainWP\Dashboard\Module\CostTracker\Cost_Tracker_DB::get_instance(); |
| 1199 | - // The producer returns a client_id => cost[] map, not a flat list. | |
| 1200 | - // get_cost_sites makes it stamp cost_sites_ids with the sites+groups+ | |
| 1201 | - // clients expansion the scope filter below needs. | |
| 1202 | - $costs_map = $cost_db->get_all_cost_trackers_by_clients( array( $client->client_id ), array( 'get_cost_sites' => true ) ); | |
| 1203 | - $costs = is_array( $costs_map ) ? ( $costs_map[ (int) $client->client_id ] ?? array() ) : array(); | |
| 1025 | + $costs = $cost_db->get_all_cost_trackers_by_clients( array( $client->client_id ) ); | |
| 1204 | 1026 | |
| 1205 | - // Any-site overlap grants the client read, but cost aggregates span | |
| 1206 | - // all the client's sites. A partial manager only sees cost items | |
| 1207 | - // backed by at least one site they can access, and company-wide | |
| 1208 | - // items with no site attachment stay unrestricted-only. | |
| 1209 | - $allowed_sites = MainWP_Abilities_Util::get_allowed_site_ids(); | |
| 1210 | - | |
| 1211 | 1027 | $items = array(); |
| 1212 | 1028 | if ( is_array( $costs ) ) { |
| 1213 | 1029 | foreach ( $costs as $cost ) { |
| 1214 | - if ( 'all' !== $allowed_sites ) { | |
| 1215 | - // A cost reached through a group or client attachment | |
| 1216 | - // carries an empty sites column; cost_sites_ids holds the | |
| 1217 | - // resolved site list. Fall back to the raw column only if | |
| 1218 | - // the producer did not stamp it. | |
| 1219 | - if ( isset( $cost->cost_sites_ids ) && is_array( $cost->cost_sites_ids ) ) { | |
| 1220 | - $cost_sites = array_map( 'intval', $cost->cost_sites_ids ); | |
| 1221 | - } else { | |
| 1222 | - $cost_sites = ! empty( $cost->sites ) ? json_decode( $cost->sites, true ) : array(); | |
| 1223 | - $cost_sites = is_array( $cost_sites ) ? array_map( 'intval', $cost_sites ) : array(); | |
| 1224 | - } | |
| 1225 | - if ( empty( array_intersect( $cost_sites, $allowed_sites ) ) ) { | |
| 1226 | - continue; | |
| 1227 | - } | |
| 1228 | - } | |
| 1229 | 1030 | $items[] = MainWP_Abilities_Util::format_cost_for_output( $cost ); |
| 1230 | 1031 | } |
| 1231 | 1032 | } |
| 1232 | 1033 | |
| @@ -1250,9 +1051,9 @@ | ||
| 1250 | 1051 | 'category' => 'mainwp-clients', |
| 1251 | 1052 | 'input_schema' => self::get_suspend_client_input_schema(), |
| 1252 | 1053 | 'output_schema' => self::get_client_output_schema(), |
| 1253 | 1054 | 'execute_callback' => array( self::class, 'execute_suspend_client' ), |
| 1254 | - 'permission_callback' => MainWP_Abilities_Util::make_operation_permission_callback( 'mainwp/suspend-client-v1', array( MainWP_Abilities_Util::class, 'check_manage_sites_permission' ) ), | |
| 1055 | + 'permission_callback' => array( MainWP_Abilities_Util::class, 'check_manage_sites_permission' ), | |
| 1255 | 1056 | 'meta' => array( |
| 1256 | 1057 | 'show_in_rest' => true, |
| 1257 | 1058 | 'annotations' => array( |
| 1258 | 1059 | 'instructions' => '', |
| @@ -1292,15 +1093,8 @@ | ||
| 1292 | 1093 | if ( is_wp_error( $client ) ) { |
| 1293 | 1094 | return $client; |
| 1294 | 1095 | } |
| 1295 | 1096 | |
| 1296 | - // Suspension affects every site the client covers: full coverage | |
| 1297 | - // required, any-site overlap is read access only. | |
| 1298 | - $access = MainWP_Abilities_Util::check_client_access( $client, $input, true ); | |
| 1299 | - if ( is_wp_error( $access ) ) { | |
| 1300 | - return $access; | |
| 1301 | - } | |
| 1302 | - | |
| 1303 | 1097 | $updated = MainWP_DB_Client::instance()->update_client( |
| 1304 | 1098 | array( |
| 1305 | 1099 | 'client_id' => $client->client_id, |
| 1306 | 1100 | 'suspended' => 1, |
| @@ -1342,9 +1136,9 @@ | ||
| 1342 | 1136 | 'category' => 'mainwp-clients', |
| 1343 | 1137 | 'input_schema' => self::get_unsuspend_client_input_schema(), |
| 1344 | 1138 | 'output_schema' => self::get_client_output_schema(), |
| 1345 | 1139 | 'execute_callback' => array( self::class, 'execute_unsuspend_client' ), |
| 1346 | - 'permission_callback' => MainWP_Abilities_Util::make_operation_permission_callback( 'mainwp/unsuspend-client-v1', array( MainWP_Abilities_Util::class, 'check_manage_sites_permission' ) ), | |
| 1140 | + 'permission_callback' => array( MainWP_Abilities_Util::class, 'check_manage_sites_permission' ), | |
| 1347 | 1141 | 'meta' => array( |
| 1348 | 1142 | 'show_in_rest' => true, |
| 1349 | 1143 | 'annotations' => array( |
| 1350 | 1144 | 'instructions' => '', |
| @@ -1382,15 +1176,8 @@ | ||
| 1382 | 1176 | $client = MainWP_Abilities_Util::resolve_client( $input['client_id_or_email'] ); |
| 1383 | 1177 | |
| 1384 | 1178 | if ( is_wp_error( $client ) ) { |
| 1385 | 1179 | return $client; |
| 1386 | - } | |
| 1387 | - | |
| 1388 | - // Same full-coverage rule as suspend: the flip affects every site | |
| 1389 | - // the client covers. | |
| 1390 | - $access = MainWP_Abilities_Util::check_client_access( $client, $input, true ); | |
| 1391 | - if ( is_wp_error( $access ) ) { | |
| 1392 | - return $access; | |
| 1393 | 1180 | } |
| 1394 | 1181 | |
| 1395 | 1182 | $updated = MainWP_DB_Client::instance()->update_client( |
| 1396 | 1183 | array( |