PluginProbe
MainWP Dashboard: Self-hosted WordPress Management for Agencies / 6.3
MainWP Dashboard: Self-hosted WordPress Management for Agencies v6.3
6.3 6.2.3 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 All 157 releases
← All changes | includes/rest-api/controller/version2/class-mainwp-rest-global-batch-controller.php +30 -28 6.2 → 6.3 View file →
@@ -44,13 +44,12 @@
44 44 * Controller names handled by the batch endpoint.
45 45 *
46 46 * @var array
47 47 */
48 - protected $controller_names = array( 'sites', 'clients', 'tags' );
48 + protected $controller_names = array( 'sites', 'clients', 'costs', 'tags' );
49 49
50 50 /**
51 - * Actions the dispatch below reads per group, each marked with the shape it reads its items in:
52 - * an 'item' action passes every item on as a request body, an 'id' action casts every item to an int.
51 + * Keep the allowed actions aligned with dispatch so supported work is not rejected or dropped.
53 52 *
54 53 * @var array
55 54 */
56 55 const GROUP_ACTIONS = array(
@@ -55,8 +54,10 @@
55 54 */
56 55 const GROUP_ACTIONS = array(
57 56 'sites' => array(
58 57 'create' => 'item',
58 + 'update' => 'item',
59 + 'delete' => 'id',
59 60 'sync' => 'id',
60 61 'reconnect' => 'id',
61 62 'disconnect' => 'id',
62 63 'suspend' => 'id',
@@ -68,11 +69,20 @@
68 69 'non-mainwp-changes' => 'id',
69 70 ),
70 71 'clients' => array(
71 72 'create' => 'item',
73 + 'update' => 'item',
74 + 'delete' => 'id',
72 75 ),
76 + 'costs' => array(
77 + 'create' => 'item',
78 + 'update' => 'item',
79 + 'delete' => 'id',
80 + ),
73 81 'tags' => array(
74 82 'create' => 'item',
83 + 'update' => 'item',
84 + 'delete' => 'id',
75 85 ),
76 86 );
77 87
78 88 /**
@@ -154,11 +164,10 @@
154 164 if ( empty( $body_groups ) || ! is_array( $body_groups ) ) {
155 165 $body_groups = (array) $request->get_body_params();
156 166 }
157 167
158 - // Groups the batch endpoint cannot dispatch (updates has no batch-capable create handler,
159 - // costs has no controller at all, anything else is unknown) are reported once each instead
160 - // of being dropped without a word or failing per item with a 405 from the core stub.
168 + // Unsupported groups would otherwise be dropped silently or fail per item with a 405
169 + // from the core stub, as updates has no batch-capable create handler.
161 170 //
162 171 // The names come from the same merged params the limit check and the dispatch read, so a
163 172 // group sent only in the query string is answered rather than counted in silence. A group is
164 173 // always an array there, so scalar params (per_page, the auth keys) name none. The body is
@@ -233,8 +242,18 @@
233 242
234 243 $controller_obj = $this->get_batch_controller( $con_name );
235 244
236 245 if ( false === $controller_obj ) {
246 + if ( ! empty( $items[ $con_name ] ) ) {
247 + $response[ $con_name ] = array(
248 + 'error' => array(
249 + 'code' => 'rest_batch_group_not_supported',
250 + /* translators: %s: batch group name */
251 + 'message' => sprintf( __( 'The %s group is not supported by the batch endpoint.', 'mainwp' ), $con_name ),
252 + 'data' => array( 'status' => 400 ),
253 + ),
254 + );
255 + }
237 256 continue;
238 257 }
239 258
240 259 if ( ! empty( $items[ $con_name ]['create'] ) ) {
@@ -524,11 +543,10 @@
524 543 * Check that a group carries only what the dispatch below can read.
525 544 *
526 545 * The dispatch indexes the group by action name and then walks each action list, so a scalar in
527 546 * either place would reach a foreach over something that is not a list, a JSON list carries none
528 - * of the names it indexes by, and an action it does not read is silently dropped. Items are
529 - * checked too: a create item is passed on as a request body and an id item is cast to an int, so
530 - * neither can be an arbitrary value.
547 + * of the names it indexes by, and an action it does not read is silently dropped. The per-item
548 + * helpers validate bodies and IDs so the response identifies each invalid item.
531 549 *
532 550 * @param string $group_name Group name.
533 551 * @param mixed $group_items Group value taken from the request.
534 552 * @return bool
@@ -560,23 +578,8 @@
560 578 // empty action dispatches nothing, and range() cannot answer for a count of zero.
561 579 if ( array() !== $action_items && array_keys( $action_items ) !== range( 0, count( $action_items ) - 1 ) ) {
562 580 return false;
563 581 }
564 -
565 - foreach ( $action_items as $item ) {
566 - if ( 'item' === $actions[ $action_name ] ) {
567 - if ( ! is_array( $item ) ) {
568 - return false;
569 - }
570 - } elseif ( ! ( is_int( $item ) && $item > 0 ) && ! ( is_string( $item ) && ctype_digit( $item ) && (int) $item > 0 && ltrim( $item, '0' ) === (string) (int) $item ) ) {
571 - // A fractional or exponent string is numeric, and the (int) cast below would read
572 - // it as a whole id the caller never asked for, so only digits are accepted. Zero
573 - // is refused with them: every site action skips an id that casts to 0, so the
574 - // batch would answer with neither an operation nor an error for that item. A digit
575 - // string past PHP_INT_MAX casts to PHP_INT_MAX, a different id than the one sent.
576 - return false;
577 - }
578 - }
579 582 }
580 583
581 584 return true;
582 585 }
@@ -590,12 +593,11 @@
590 593 protected function check_batch_limit( $items ) { //phpcs:ignore -- NOSONAR complex function.
591 594 $limit = apply_filters( 'mainwp_rest_batch_items_limit', 100, $this->get_normalized_rest_base() );
592 595 $total = 0;
593 596
594 - // The updates and costs groups are rejected as a whole by batch_items(), but their items
595 - // still count toward the cap so an oversized request is refused before anything else is
596 - // dispatched.
597 - $count_names = array_merge( $this->controller_names, array( 'updates', 'costs' ) );
597 + // The updates group is rejected as a whole by batch_items(), but its items still count
598 + // toward the cap so an oversized request is refused before anything else is dispatched.
599 + $count_names = array_merge( $this->controller_names, array( 'updates' ) );
598 600
599 601 foreach ( $count_names as $con_name ) {
600 602 if ( ! empty( $items[ $con_name ] ) && is_countable( $items[ $con_name ] ) ) {
601 603 if ( ! empty( $items[ $con_name ]['create'] ) && is_countable( $items[ $con_name ]['create'] ) ) {