PluginProbe
Social Media Auto Poster – Schedule & Publish to Buffer / 6.2.5
Social Media Auto Poster – Schedule & Publish to Buffer v6.2.5
6.2.5 6.2.4 6.2.3 6.2.2 6.2.1 6.2.0 6.1.2 6.1.1 6.1.0 6.0.9 6.0.8 6.0.7 6.0.6 6.0.5 6.0.4 6.0.3 6.0.2 6.0.1 6.0.0 3.8.1 3.8.2 3.8.3 3.8.4 3.8.5 3.8.6 All 126 releases
← All changes | includes/class-buffer-api.php +144 -112 6.2.16.2.5 View file →
@@ -21,9 +21,9 @@
21 21 * Holds the base class object.
22 22 *
23 23 * @since 3.4.7
24 24 *
25 - * @var object.
25 + * @var object
26 26 */
27 27 public $base;
28 28
29 29 /**
@@ -30,9 +30,9 @@
30 30 * Holds the Buffer Application's Client ID
31 31 *
32 32 * @since 3.3.3
33 33 *
34 - * @var string.
34 + * @var string
35 35 */
36 36 private $client_id = 'd1Lk26lma4iEgb-20v1BWmdKrlopiGwP9pu9ri7JG0e';
37 37
38 38 /**
@@ -39,9 +39,9 @@
39 39 * Holds the oAuth Authorize URL
40 40 *
41 41 * @since 6.0.0
42 42 *
43 - * @var string.
43 + * @var string
44 44 */
45 45 private $oauth_authorize_url = 'https://auth.buffer.com/';
46 46
47 47 /**
@@ -48,27 +48,18 @@
48 48 * Holds the oAuth Gateway endpoint, used to exchange a code for an access token
49 49 *
50 50 * @since 3.3.3
51 51 *
52 - * @var string.
52 + * @var string
53 53 */
54 54 private $redirect_uri = 'https://www.wpzinc.com/?oauth=bufferv2';
55 55
56 56 /**
57 - * Holds the Proxy endpoint, which might be used to pass requests through
58 - *
59 - * @since 4.2.1
60 - *
61 - * @var string.
62 - */
63 - private $proxy_endpoint = 'https://proxy.wpzinc.net/';
64 -
65 - /**
66 57 * Holds the API endpoint
67 58 *
68 59 * @since 3.4.7
69 60 *
70 - * @var string.
61 + * @var string
71 62 */
72 63 private $api_endpoint = 'https://api.buffer.com/';
73 64
74 65 /**
@@ -93,9 +84,9 @@
93 84 * Token Expiry Timestamp
94 85 *
95 86 * @since 3.5.0
96 87 *
97 - * @var int
88 + * @var int|bool
98 89 */
99 90 public $token_expires = false;
100 91
101 92 /**
@@ -125,9 +116,9 @@
125 116 ?>
126 117 <div class="wpzinc-option">
127 118 <div class="full">
128 119 <a href="<?php echo esc_attr( $this->get_oauth_url() ); ?>" class="button button-primary">
129 - <?php esc_html_e( 'Connect a Buffer Account', 'wp-to-buffer' ); ?>
120 + <?php esc_html_e( 'Connect an additional Buffer Account', 'wp-to-buffer' ); ?>
130 121 </a>
131 122 </div>
132 123 </div>
133 124 <?php
@@ -340,8 +331,13 @@
340 331 * @param bool|int $token_expires Token Expiry.
341 332 */
342 333 public function set_tokens( $access_token = '', $refresh_token = '', $token_expires = false ) {
343 334
335 + $this->base->get_class( 'log' )->add_to_debug_log( 'Buffer API: set_tokens(): Started.' );
336 + $this->base->get_class( 'log' )->add_to_debug_log( 'Buffer API: set_tokens(): access_token = ' . $access_token );
337 + $this->base->get_class( 'log' )->add_to_debug_log( 'Buffer API: set_tokens(): refresh_token = ' . $refresh_token );
338 + $this->base->get_class( 'log' )->add_to_debug_log( 'Buffer API: set_tokens(): token_expires = ' . $token_expires );
339 +
344 340 $this->access_token = $access_token;
345 341 $this->refresh_token = $refresh_token;
346 342 $this->token_expires = $token_expires;
347 343
@@ -352,9 +348,9 @@
352 348 *
353 349 * @since 6.0.0
354 350 *
355 351 * @param string $authorization_code Authorization Code, returned from get_oauth_url() flow.
356 - * @return WP_Error|array
352 + * @return \WP_Error|array
357 353 */
358 354 public function get_access_token( $authorization_code ) {
359 355
360 356 $result = $this->oauth_request(
@@ -397,18 +393,30 @@
397 393 * @since 2.0.0
398 394 */
399 395 public function refresh_token() {
400 396
401 - // Bail if no refresh token is available to use, otherwise we'll
402 - // send a request to Buffer with an empty refresh_token, which
403 - // will fail.
397 + $this->base->get_class( 'log' )->add_to_debug_log( 'Buffer API: refresh_token(): Started.' );
398 +
399 + // Bail if we don't have a refresh token.
404 400 if ( empty( $this->refresh_token ) ) {
405 - return new \WP_Error(
406 - $this->base->plugin->filter_name . '_api_refresh_token_error',
407 - __( 'No refresh token available; cannot refresh access token.', 'wp-to-buffer' )
408 - );
401 + $this->base->get_class( 'log' )->add_to_debug_log( 'Buffer API: refresh_token(): Error: No refresh token available; cannot refresh.' );
402 + return new \WP_Error( 'missing_refresh_token', __( 'No refresh token exists', 'wp-to-buffer' ) );
409 403 }
410 404
405 + $this->base->get_class( 'log' )->add_to_debug_log( 'Buffer API: refresh_token(): access_token = ' . $this->access_token );
406 + $this->base->get_class( 'log' )->add_to_debug_log( 'Buffer API: refresh_token(): refresh_token = ' . $this->refresh_token );
407 + $this->base->get_class( 'log' )->add_to_debug_log( 'Buffer API: refresh_token(): token_expires = ' . $this->token_expires );
408 + $this->base->get_class( 'log' )->add_to_debug_log( 'Buffer API: refresh_token(): token_expires = ' . ( $this->token_expires && time() > (int) $this->token_expires ? 'expired ' . ( time() - (int) $this->token_expires ) . 's ago' : 'expires in ' . ( (int) $this->token_expires - time() ) . 's' ) );
409 +
410 + // Bail if the access token hasn't yet expired.
411 + if ( strtotime( '+15 minutes' ) < $this->token_expires ) {
412 + $this->base->get_class( 'log' )->add_to_debug_log( 'Buffer API: refresh_token(): Skipped: Access token not yet within the 15 minute refresh window.' );
413 + return false;
414 + }
415 +
416 + $this->base->get_class( 'log' )->add_to_debug_log( 'Buffer API: refresh_token(): Requesting new access token.' );
417 +
418 + // Send request.
411 419 $result = $this->oauth_request(
412 420 $this->oauth_authorize_url . 'token',
413 421 array(
414 422 'client_id' => $this->client_id,
@@ -418,14 +426,16 @@
418 426 );
419 427
420 428 // If an error occured, log and return it now.
421 429 if ( is_wp_error( $result ) ) {
430 + $this->base->get_class( 'log' )->add_to_debug_log( sprintf( 'Buffer API: refresh_token(): Error: [%s] %s', $result->get_error_code(), $result->get_error_message() ) );
431 +
422 432 /**
423 433 * Perform any actions when refreshing an expired access token fails.
424 434 *
425 435 * @since 6.0.0
426 436 *
427 - * @param WP_Error $result Error from API.
437 + * @param \WP_Error $result Error from API.
428 438 * @param string $client_id OAuth Client ID.
429 439 * @param string $access_token Access Token.
430 440 * @param string $refresh_token Refresh Token.
431 441 */
@@ -440,8 +450,14 @@
440 450 'refresh_token' => $result['refresh_token'],
441 451 'token_expires' => strtotime( '+' . $result['expires_in'] . ' seconds' ),
442 452 );
443 453
454 + $this->base->get_class( 'log' )->add_to_debug_log( 'Buffer API: refresh_token(): Success' );
455 + $this->base->get_class( 'log' )->add_to_debug_log( 'Buffer API: refresh_token(): New access_token = ' . $result['access_token'] );
456 + $this->base->get_class( 'log' )->add_to_debug_log( 'Buffer API: refresh_token(): New refresh_token = ' . $result['refresh_token'] );
457 + $this->base->get_class( 'log' )->add_to_debug_log( 'Buffer API: refresh_token(): New token_expires = ' . $result['token_expires'] );
458 + $this->base->get_class( 'log' )->add_to_debug_log( 'Buffer API: refresh_token(): New token_expires = ' . ( $result['token_expires'] && time() > (int) $result['token_expires'] ? 'expired ' . ( time() - (int) $result['token_expires'] ) . 's ago' : 'expires in ' . ( (int) $result['token_expires'] - time() ) . 's' ) );
459 +
444 460 /**
445 461 * Perform any actions with the new access token, such as saving it.
446 462 *
447 463 * @since 6.0.0
@@ -467,9 +483,9 @@
467 483 * @since 6.0.1
468 484 *
469 485 * @param bool $force Force API call (false = use stored option).
470 486 *
471 - * @return WP_Error|array
487 + * @return \WP_Error|array
472 488 */
473 489 public function organizations( $force = false ) {
474 490
475 491 // Return stored organizations from the non-autoloaded option, unless
@@ -529,9 +545,9 @@
529 545 *
530 546 * @since 6.0.0
531 547 *
532 548 * @param string $account_id Account ID.
533 - * @return WP_Error|array
549 + * @return \WP_Error|array
534 550 */
535 551 public function account( $account_id = '' ) {
536 552
537 553 $organizations = $this->organizations( false );
@@ -560,9 +576,9 @@
560 576 * @since 3.0.0
561 577 *
562 578 * @param bool $force Force API call (false = use stored option).
563 579 * @param string $account_id Account ID.
564 - * @return WP_Error|array
580 + * @return \WP_Error|array
565 581 */
566 582 public function profiles( $force = false, $account_id = 'default' ) {
567 583
568 584 // Return stored profiles if available and not forcing a refresh.
@@ -568,11 +584,14 @@
568 584 // Return stored profiles if available and not forcing a refresh.
569 585 $option_name = $this->base->plugin->name . '-profiles-' . $account_id;
570 586 $profiles = get_option( $option_name );
571 587 if ( ! $force && is_array( $profiles ) ) {
588 + $this->base->get_class( 'log' )->add_to_debug_log( sprintf( 'Buffer API: profiles(): account=%s: returning %d cached profile(s).', $account_id, count( $profiles ) ) );
572 589 return $profiles;
573 590 }
574 591
592 + $this->base->get_class( 'log' )->add_to_debug_log( sprintf( 'Buffer API: profiles(): account=%s: fetching profiles from Buffer (force=%s).', $account_id, $force ? 'yes' : 'no' ) );
593 +
575 594 // Build GraphQL query.
576 595 $query = '
577 596 query GetChannels($organizationId: OrganizationId!) {
578 597 channels(input: { organizationId: $organizationId }) {
@@ -603,8 +622,9 @@
603 622 );
604 623
605 624 // Check for errors.
606 625 if ( is_wp_error( $results ) ) {
626 + $this->base->get_class( 'log' )->add_to_debug_log( sprintf( 'Buffer API: profiles(): account=%s: FAILED to fetch profiles: [%s] %s', $account_id, $results->get_error_code(), $results->get_error_message() ) );
607 627 return $results;
608 628 }
609 629
610 630 // Build profiles array from results.
@@ -647,9 +667,9 @@
647 667 *
648 668 * @since 6.0.0
649 669 *
650 670 * @param string $post_id Post ID.
651 - * @return WP_Error|array
671 + * @return \WP_Error|array
652 672 */
653 673 public function get_post( $post_id ) {
654 674
655 675 $query = '
@@ -693,9 +713,9 @@
693 713 * @since 3.0.0
694 714 *
695 715 * @param array $params Params.
696 716 * @param string $service Service.
697 - * @return WP_Error|array
717 + * @return \WP_Error|array
698 718 */
699 719 public function updates_create( $params, $service ) {
700 720
701 721 // Build GraphQL variables.
@@ -751,9 +771,9 @@
751 771 switch ( $service ) {
752 772
753 773 case 'instagram':
754 774 $metadata = array(
755 - 'type' => $params['post_type'] === 'story' ? 'story' : 'post',
775 + 'type' => in_array( $params['post_type'], array( 'story', 'video_story' ), true ) ? 'story' : 'post',
756 776 'shouldShareToFeed' => true,
757 777 );
758 778
759 779 // First Comment.
@@ -759,8 +779,13 @@
759 779 // First Comment.
760 780 if ( ! empty( $params['first_comment'] ) ) {
761 781 $metadata['firstComment'] = $params['first_comment'];
762 782 }
783 +
784 + // Shop Grid Link.
785 + if ( ! empty( $params['url'] ) ) {
786 + $metadata['link'] = $params['url'];
787 + }
763 788 break;
764 789
765 790 case 'facebook':
766 791 $metadata = array(
@@ -773,14 +798,10 @@
773 798 }
774 799
775 800 // OpenGraph / Link Attachment.
776 801 if ( $params['post_type'] === 'link' && ! empty( $params['url'] ) ) {
777 - $assets = array(
778 - array(
779 - 'link' => array(
780 - 'url' => $params['url'],
781 - ),
782 - ),
802 + $metadata['linkAttachment'] = array(
803 + 'url' => $params['url'],
783 804 );
784 805 }
785 806
786 807 // Annotations.
@@ -796,14 +817,10 @@
796 817 }
797 818
798 819 // OpenGraph / Link Attachment.
799 820 if ( $params['post_type'] === 'link' && ! empty( $params['url'] ) ) {
800 - $assets = array(
801 - array(
802 - 'link' => array(
803 - 'url' => $params['url'],
804 - ),
805 - ),
821 + $metadata['linkAttachment'] = array(
822 + 'url' => $params['url'],
806 823 );
807 824 }
808 825
809 826 // Annotations.
@@ -814,13 +831,11 @@
814 831
815 832 case 'twitter':
816 833 // First Comment.
817 834 if ( ! empty( $params['first_comment'] ) ) {
818 - $metadata = array(
819 - 'thread' => array(
820 - array(
821 - 'text' => $params['first_comment'],
822 - ),
835 + $metadata['thread'] = array(
836 + array(
837 + 'text' => $params['first_comment'],
823 838 ),
824 839 );
825 840 }
826 841
@@ -878,24 +893,18 @@
878 893
879 894 case 'threads':
880 895 // OpenGraph / Link Attachment.
881 896 if ( $params['post_type'] === 'link' && ! empty( $params['url'] ) ) {
882 - $assets = array(
883 - array(
884 - 'link' => array(
885 - 'url' => $params['url'],
886 - ),
887 - ),
897 + $metadata['linkAttachment'] = array(
898 + 'url' => $params['url'],
888 899 );
889 900 }
890 901
891 902 // First Comment.
892 903 if ( ! empty( $params['first_comment'] ) ) {
893 - $metadata = array(
894 - 'thread' => array(
895 - array(
896 - 'text' => $params['first_comment'],
897 - ),
904 + $metadata['thread'] = array(
905 + array(
906 + 'text' => $params['first_comment'],
898 907 ),
899 908 );
900 909 }
901 910 break;
@@ -902,24 +911,18 @@
902 911
903 912 case 'bluesky':
904 913 // OpenGraph / Link Attachment.
905 914 if ( $params['post_type'] === 'link' && ! empty( $params['url'] ) ) {
906 - $assets = array(
907 - array(
908 - 'link' => array(
909 - 'url' => $params['url'],
910 - ),
911 - ),
915 + $metadata['linkAttachment'] = array(
916 + 'url' => $params['url'],
912 917 );
913 918 }
914 919
915 920 // First Comment.
916 921 if ( ! empty( $params['first_comment'] ) ) {
917 - $metadata = array(
918 - 'thread' => array(
919 - array(
920 - 'text' => $params['first_comment'],
921 - ),
922 + $metadata['thread'] = array(
923 + array(
924 + 'text' => $params['first_comment'],
922 925 ),
923 926 );
924 927 }
925 928 break;
@@ -926,13 +929,11 @@
926 929
927 930 case 'mastodon':
928 931 // First Comment.
929 932 if ( ! empty( $params['first_comment'] ) ) {
930 - $metadata = array(
931 - 'thread' => array(
932 - array(
933 - 'text' => $params['first_comment'],
934 - ),
933 + $metadata['thread'] = array(
934 + array(
935 + 'text' => $params['first_comment'],
935 936 ),
936 937 );
937 938 }
938 939
@@ -973,18 +974,31 @@
973 974 'image' => array(
974 975 'url' => $media['image'],
975 976 'thumbnailUrl' => $media['thumbnail'],
976 977 'metadata' => array(
977 - 'altText' => $media['alt_text'],
978 - 'dimensions' => array(
979 - 'width' => $media['width'],
980 - 'height' => $media['height'],
981 - ),
978 + 'altText' => $media['alt_text'],
982 979 ),
983 980 ),
984 981 );
985 982 }
986 983 break;
984 +
985 + case 'video':
986 + case 'video_story':
987 + // Bail if no video URL is defined.
988 + if ( empty( $params['video_url'] ) ) {
989 + break;
990 + }
991 +
992 + // Buffer fetches the video from a public URL, so we only send the URL.
993 + $assets = array(
994 + array(
995 + 'video' => array(
996 + 'url' => $params['video_url'],
997 + ),
998 + ),
999 + );
1000 + break;
987 1001 }
988 1002
989 1003 // Include assets. Always overwrites the default empty array
990 1004 // initialised above, if the service branch built any.
@@ -1061,9 +1075,9 @@
1061 1075 * @since 3.0.0
1062 1076 *
1063 1077 * @param string $url URL.
1064 1078 * @param array $params Parameters (optional).
1065 - * @return WP_Error|array
1079 + * @return \WP_Error|array
1066 1080 */
1067 1081 private function oauth_request( $url, $params = array() ) {
1068 1082
1069 1083 // Send request.
@@ -1100,13 +1114,40 @@
1100 1114 * @since 6.0.0
1101 1115 *
1102 1116 * @param string $query GraphQL Query.
1103 1117 * @param array $variables GraphQL Variables.
1104 - * @param bool $is_retry Whether this is a retry following a token refresh.
1105 - * @return WP_Error|array
1118 + * @return \WP_Error|array
1106 1119 */
1107 - private function graphql_query( $query, $variables = array(), $is_retry = false ) {
1120 + private function graphql_query( $query, $variables = array() ) {
1108 1121
1122 + $this->base->get_class( 'log' )->add_to_debug_log( 'Buffer API: graphql_query(): Started.' );
1123 + $this->base->get_class( 'log' )->add_to_debug_log( 'Buffer API: graphql_query(): query = ' . $query );
1124 + $this->base->get_class( 'log' )->add_to_debug_log( 'Buffer API: graphql_query(): variables = ' . print_r( $variables, true ) ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_print_r
1125 + $this->base->get_class( 'log' )->add_to_debug_log( 'Buffer API: graphql_query(): access_token = ' . $this->access_token );
1126 + $this->base->get_class( 'log' )->add_to_debug_log( 'Buffer API: graphql_query(): refresh_token = ' . $this->refresh_token );
1127 + $this->base->get_class( 'log' )->add_to_debug_log( 'Buffer API: graphql_query(): token_expires = ' . $this->token_expires );
1128 + $this->base->get_class( 'log' )->add_to_debug_log( 'Buffer API: graphql_query(): token_expires = ' . ( $this->token_expires && time() > (int) $this->token_expires ? 'expired ' . ( time() - (int) $this->token_expires ) . 's ago' : 'expires in ' . ( (int) $this->token_expires - time() ) . 's' ) );
1129 +
1130 + // Check required parameters exist.
1131 + if ( empty( $this->access_token ) ) {
1132 + $this->base->get_class( 'log' )->add_to_debug_log( 'Buffer API: graphql_query(): Error: No access token was specified' );
1133 + return new \WP_Error( 'missing_access_token', __( 'No access token was specified', 'wp-to-buffer' ) );
1134 + }
1135 +
1136 + // Fetch a new access token and refresh token.
1137 + $result = $this->refresh_token();
1138 +
1139 + // Bail if something went wrong.
1140 + if ( is_wp_error( $result ) ) {
1141 + $this->base->get_class( 'log' )->add_to_debug_log( 'Buffer API: graphql_query(): Error: ' . $result->get_error_message() );
1142 + return $result;
1143 + }
1144 +
1145 + $this->base->get_class( 'log' )->add_to_debug_log( 'Buffer API: graphql_query(): access_token = ' . $this->access_token );
1146 + $this->base->get_class( 'log' )->add_to_debug_log( 'Buffer API: graphql_query(): refresh_token = ' . $this->refresh_token );
1147 + $this->base->get_class( 'log' )->add_to_debug_log( 'Buffer API: graphql_query(): token_expires = ' . $this->token_expires );
1148 + $this->base->get_class( 'log' )->add_to_debug_log( 'Buffer API: graphql_query(): token_expires = ' . ( $this->token_expires && time() > (int) $this->token_expires ? 'expired ' . ( time() - (int) $this->token_expires ) . 's ago' : 'expires in ' . ( (int) $this->token_expires - time() ) . 's' ) );
1149 +
1109 1150 // Build body.
1110 1151 $body = array( 'query' => $query );
1111 1152 if ( ! empty( $variables ) ) {
1112 1153 $body['variables'] = $variables;
@@ -1124,41 +1165,22 @@
1124 1165 );
1125 1166
1126 1167 // If an error occured, return it now.
1127 1168 if ( is_wp_error( $result ) ) {
1169 + $this->base->get_class( 'log' )->add_to_debug_log( sprintf( 'Buffer API: graphql_query(): Error: [%s] %s', $result->get_error_code(), $result->get_error_message() ) );
1128 1170 return $result;
1129 1171 }
1130 1172
1131 - // Parse result.
1173 + // Parse the response.
1132 1174 $response = $this->parse_response( $result );
1133 1175
1134 - // If this is a retry, return the parsed response.
1135 - // This prevents an infinite loop of retries when a token refresh fails.
1136 - if ( $is_retry ) {
1137 - return $response;
1176 + // Log any API error, so failures that would otherwise be silent are captured.
1177 + if ( is_wp_error( $response ) ) {
1178 + $this->base->get_class( 'log' )->add_to_debug_log( sprintf( 'Buffer API: graphql_query(): API returned error: [%s] %s', $response->get_error_code(), $response->get_error_message() ) );
1138 1179 }
1139 1180
1140 - // If the request was successful, return the response.
1141 - if ( ! is_wp_error( $response ) ) {
1142 - return $response;
1143 - }
1181 + return $response;
1144 1182
1145 - // If the error isn't an unauthenticated error, return it.
1146 - if ( strtolower( $response->get_error_code() ) !== 'unauthenticated' ) {
1147 - return $response;
1148 - }
1149 -
1150 - // Attempt to refresh the token.
1151 - $refresh_result = $this->refresh_token();
1152 -
1153 - // Bail if the refresh token attempt failed.
1154 - if ( is_wp_error( $refresh_result ) ) {
1155 - return $refresh_result;
1156 - }
1157 -
1158 - // Attempt the request again, now we have a new access token.
1159 - return $this->graphql_query( $query, $variables, true );
1160 -
1161 1183 }
1162 1184
1163 1185 /**
1164 1186 * Returns the headers to use in an authenticated GraphQL API request.
@@ -1245,15 +1267,15 @@
1245 1267
1246 1268 }
1247 1269
1248 1270 /**
1249 - * Parses the response body, returning a WP_Error
1271 + * Parses the response body, returning a \WP_Error
1250 1272 * if the response body contains an error.
1251 1273 *
1252 1274 * @since 3.9.8
1253 1275 *
1254 - * @param string $response Response Body.
1255 - * @return WP_Error|array
1276 + * @param array|\WP_Error $response HTTP Response.
1277 + * @return \WP_Error|array
1256 1278 */
1257 1279 private function parse_response( $response ) {
1258 1280
1259 1281 // Get HTTP code and body.
@@ -1268,10 +1290,20 @@
1268 1290 '403 Forbidden'
1269 1291 );
1270 1292 }
1271 1293
1294 + // Retain-and-retry on server errors: return without touching stored tokens.
1295 + if ( $http_code >= 500 ) {
1296 + return new \WP_Error( 'buffer_api_server_error', $http_code . ' server error' );
1297 + }
1298 +
1272 1299 // Decode response.
1273 1300 $body = json_decode( $http_body, true );
1301 +
1302 + // Bail if the response isn't valid JSON.
1303 + if ( ! is_array( $body ) ) {
1304 + return new \WP_Error( 'buffer_api_invalid_response', 'Invalid API response' );
1305 + }
1274 1306
1275 1307 // If an error is detected, return it.
1276 1308 if ( array_key_exists( 'error', $body ) ) {
1277 1309 return new \WP_Error(