PluginProbe
Social Media Auto Poster – Schedule & Publish to Buffer / 3.8.3
Social Media Auto Poster – Schedule & Publish to Buffer v3.8.3
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 3.8.7 3.8.8 All 124 releases
wp-to-buffer / includes / admin / buffer-api.php

buffer-api.php in Social Media Auto Poster – Schedule & Publish to Buffer 3.8.3, at includes/admin/buffer-api.php

908 lines 29.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Buffer API class
4 *
5 * @package WP_To_Social_Pro
6 * @author Tim Carr
7 * @version 3.0.0
8 */
9 class WP_To_Social_Pro_Buffer_API {
10
11 /**
12 * Holds the base class object.
13 *
14 * @since 3.4.7
15 *
16 * @var object
17 */
18 public $base;
19
20 /**
21 * Holds the Buffer Application's Client ID
22 *
23 * @since 3.3.3
24 *
25 * @var string
26 */
27 private $client_id = '592d41d14d97ab7e4e571edb';
28
29 /**
30 * Holds the oAuth Gateway endpoint, used to exchange a code for an access token
31 *
32 * @since 3.3.3
33 *
34 * @var string
35 */
36 private $oauth_gateway_endpoint = 'https://www.wpzinc.com/?oauth=buffer';
37
38 /**
39 * Holds the Proxy endpoint, which might be used to pass requests through
40 *
41 * @since 4.2.1
42 *
43 * @var string
44 */
45 private $proxy_endpoint = 'https://proxy.wpzinc.net/';
46
47 /**
48 * Holds the API endpoint
49 *
50 * @since 3.4.7
51 *
52 * @var string
53 */
54 private $api_endpoint = 'https://api.bufferapp.com/';
55
56 /**
57 * Holds the API version
58 *
59 * @since 3.4.7
60 *
61 * @var int
62 */
63 private $api_version = '1';
64
65 /**
66 * Access Token
67 *
68 * @since 3.0.0
69 *
70 * @var string
71 */
72 public $access_token = '';
73
74 /**
75 * Refresh Token
76 *
77 * @since 3.4.7
78 *
79 * @var string
80 */
81 public $refresh_token = '';
82
83 /**
84 * Token Expiry Timestamp
85 *
86 * @since 3.5.0
87 *
88 * @var int
89 */
90 public $token_expires = false;
91
92 /**
93 * Constructor
94 *
95 * @since 3.4.7
96 *
97 * @param object $base Base Plugin Class
98 */
99 public function __construct( $base ) {
100
101 // Store base class
102 $this->base = $base;
103
104 add_action( 'wp_to_buffer_output_auth', array( $this, 'output_oauth' ) );
105 add_action( 'wp_to_buffer_pro_output_auth', array( $this, 'output_oauth' ) );
106
107 }
108
109 /**
110 * Outputs an Authorize Plugin button on Settings > General when the Plugin needs to be authenticated with Buffer.
111 *
112 * @since 4.2.0
113 */
114 public function output_oauth() {
115
116 ?>
117 <div class="wpzinc-option">
118 <div class="full">
119 <a href="<?php echo $this->get_oauth_url(); ?>" class="button button-primary">
120 <?php _e( 'Authorize Plugin', 'wp-to-social-pro' ); ?>
121 </a>
122 </div>
123 </div>
124 <?php
125
126 }
127
128 /**
129 * Returns the oAuth 2 URL used to begin the oAuth process
130 *
131 * @since 3.3.3
132 *
133 * @return string oAuth URL
134 */
135 public function get_oauth_url() {
136
137 // Return oAuth URL
138 return 'https://bufferapp.com/oauth2/authorize?client_id=' . $this->client_id . '&redirect_uri=' . urlencode( $this->oauth_gateway_endpoint ) . '&response_type=code&state=' . urlencode( admin_url( 'admin.php?page=' . $this->base->plugin->name . '-settings' ) );
139
140 }
141
142 /**
143 * Returns the Buffer URL where the user can register for a Buffer account
144 *
145 * @since 4.6.4
146 *
147 * @return string URL
148 */
149 public function get_registration_url() {
150
151 return 'https://login.buffer.com/signup?product=publish&plan=free';
152
153 }
154
155 /**
156 * Returns the Buffer URL where the user can connect their social media accounts
157 * to Buffer
158 *
159 * @since 3.8.4
160 *
161 * @return string URL
162 */
163 public function get_connect_profiles_url() {
164
165 // Return Connect Profiles URL
166 return 'https://account.buffer.com/channels/connect';
167
168 }
169
170 /**
171 * Returns the Buffer URL where the user can change the timezone for the
172 * given profile ID.
173 *
174 * @since 3.8.1
175 *
176 * @param string $profile_id Profile ID
177 * @return string Timezone Settings URL
178 */
179 public function get_timezone_settings_url( $profile_id ) {
180
181 return 'https://publish.buffer.com/profile/' . $profile_id . '/tab/settings/postingSchedule';
182
183 }
184
185 /**
186 * Sets this class' access and refresh tokens
187 *
188 * @since 3.4.0
189 *
190 * @param string $access_token Access Token
191 * @param string $refresh_token Refresh Token
192 * @param mixed $token_expires Token Expires (false | timestamp)
193 */
194 public function set_tokens( $access_token = '', $refresh_token = '', $token_expires = false ) {
195
196 $this->access_token = $access_token;
197 $this->refresh_token = $refresh_token;
198 $this->token_expires = $token_expires;
199
200 }
201
202 /**
203 * Checks if an access token was set. Called by any function which
204 * performs a call to the API
205 *
206 * @since 3.5.0
207 *
208 * @return bool Token Exists
209 */
210 private function check_access_token_exists() {
211
212 if ( empty( $this->access_token ) ) {
213 return false;
214 }
215
216 return true;
217
218 }
219
220 /**
221 * Checks if a refresh token was set. Called by any function which
222 * performs a call to the API
223 *
224 * @since 3.5.0
225 *
226 * @return bool Token Exists
227 */
228 private function check_refresh_token_exists() {
229
230 if ( empty( $this->refresh_token ) ) {
231 return false;
232 }
233
234 return true;
235
236 }
237
238 /**
239 * Returns the User object
240 *
241 * @since 3.0.0
242 *
243 * @return mixed WP_Error | User object
244 */
245 public function user() {
246
247 // Check access token
248 if ( ! $this->check_access_token_exists() ) {
249 return false;
250 }
251
252 return $this->get( 'user.json' );
253
254 }
255
256 /**
257 * Returns a list of Social Media Profiles attached to the Buffer Account.
258 *
259 * @since 3.0.0
260 *
261 * @param bool $force Force API call (false = use WordPress transient)
262 * @param int $transient_expiration_time Transient Expiration Time, in seconds (default: 12 hours)
263 * @return mixed WP_Error | Profiles object
264 */
265 public function profiles( $force = false, $transient_expiration_time = 43200 ) {
266
267 // Check access token
268 if ( ! $this->check_access_token_exists() ) {
269 return false;
270 }
271
272 // Setup profiles array
273 $profiles = array();
274
275 // Check if our WordPress transient already has this data.
276 // This reduces the number of times we query the API
277 if ( $force || false === ( $profiles = get_transient( $this->base->plugin->name . '_buffer_api_profiles' ) ) ) {
278 // Get profiles
279 $results = $this->get( 'profiles.json?subprofiles=1' );
280
281 // Check for errors
282 if ( is_wp_error( $results ) ) {
283 return $results;
284 }
285
286 // Check data is valid
287 foreach ( $results as $result ) {
288 // We don't support Instagram or Pinterest in the Free version
289 if ( class_exists( 'WP_To_Buffer' ) ) {
290 if ( $result->service == 'instagram' || $result->service == 'pinterest' ) {
291 continue;
292 }
293 }
294
295 // Add profile to array
296 $profiles[ $result->id ] = array(
297 'id' => $result->id, // Buffer ID
298 'social_network_id' => $result->service_id, // Social Network (e.g. FB, Twitter) ID
299 'formatted_service' => $result->formatted_service,
300 'formatted_username'=> ( $result->service != 'twitter' ? $result->formatted_username : '' ),
301 'service' => $result->service,
302 'timezone' => $result->timezone,
303 'can_be_subprofile' => false, // For pinterest, the profile is the account, not the board
304 );
305
306 // Twitter's 2019 Developer Policies mean that the formatted username and profile image are no longer returned.
307 // In turn, Buffer cannot provide this information, so we must directly query for it through the Twitter API.
308 if ( $result->service == 'twitter' && empty( $profiles[ $result->id ]['formatted_username'] ) ) {
309 // Fetch Twitter username from the API
310 // The API class will check the transient first and use cached results if available
311 $twitter_username = $this->base->get_class( 'twitter_api' )->get_username_by_id( $profiles[ $result->id ]['social_network_id'], $transient_expiration_time );
312 if ( is_wp_error( $twitter_username ) ) {
313 continue;
314 }
315
316 // Add username to results
317 $profiles[ $result->id ]['formatted_username'] = $twitter_username;
318 }
319
320 // Pinterest: Add subprofiles
321 if ( $result->service == 'pinterest' ) {
322 $profiles[ $result->id ]['subprofiles'] = array();
323
324 if ( isset( $result->subprofiles ) && count( $result->subprofiles ) > 0 ) {
325 foreach ( $result->subprofiles as $sub_profile ) {
326 $profiles[ $result->id ]['subprofiles'][ $sub_profile->id ] = array(
327 'id' => $sub_profile->id,
328 'name' => $sub_profile->name,
329 'service' => $sub_profile->service,
330 );
331 }
332 }
333 }
334 }
335
336 // Store profiles in transient
337 set_transient( $this->base->plugin->name . '_buffer_api_profiles', $profiles, $transient_expiration_time );
338 }
339
340 // Return results
341 return $profiles;
342
343 }
344
345 /**
346 * Returns an individual update (status) for the given ID
347 *
348 * @since 4.5.6
349 *
350 * @param string $id Update ID
351 * @return mixed WP_Error | Update object
352 */
353 public function updates_get( $id ) {
354
355 // Check access token
356 if ( ! $this->check_access_token_exists() ) {
357 return false;
358 }
359
360 return $this->get( '/updates/' . $id . '.json' );
361
362 }
363
364 /**
365 * Returns an array of status update(s) that are queued for the given Profile ID
366 *
367 * @since 4.5.6
368 *
369 * @param string $profile_id Profile ID
370 * @return mixed WP_Error | Updates array
371 */
372 public function profiles_updates_pending( $profile_id ) {
373
374 // Check access token
375 if ( ! $this->check_access_token_exists() ) {
376 return false;
377 }
378
379 return $this->get( '/profiles/' . $profile_id . '/updates/pending.json' );
380
381 }
382
383 /**
384 * Creates an update (status)
385 *
386 * @since 3.0.0
387 *
388 * @param array $params Params
389 * @return mixed WP_Error | Update object
390 */
391 public function updates_create( $params ) {
392
393 // Check access token
394 if ( ! $this->check_access_token_exists() ) {
395 return false;
396 }
397
398 // Send request
399 $result = $this->post( 'updates/create.json', $params );
400
401 // Bail if the result is an error
402 if ( is_wp_error( $result ) ) {
403 return $result;
404 }
405
406 // Return array of just the data we need to send to the Plugin
407 return array(
408 'profile_id' => $result->updates[0]->profile_id,
409 'message' => $result->message,
410 'status_text' => $result->updates[0]->text,
411 'status_created_at' => $result->updates[0]->created_at,
412 'due_at' => $result->updates[0]->due_at,
413 );
414
415 }
416
417 /**
418 * Private function to perform a GET request
419 *
420 * @since 3.0.0
421 *
422 * @param string $cmd Command (required)
423 * @param array $params Params (optional)
424 * @return mixed WP_Error | object
425 */
426 private function get( $cmd, $params = array() ) {
427
428 return $this->request( $cmd, 'get', $params );
429
430 }
431
432 /**
433 * Private function to perform a POST request
434 *
435 * @since 3.0.0
436 *
437 * @param string $cmd Command (required)
438 * @param array $params Params (optional)
439 * @return mixed WP_Error | object
440 */
441 private function post( $cmd, $params = array() ) {
442
443 return $this->request( $cmd, 'post', $params );
444
445 }
446
447 /**
448 * Main function which handles sending requests to the Buffer API
449 *
450 * @since 3.0.0
451 *
452 * @param string $cmd Command
453 * @param string $method Method (get|post)
454 * @param array $params Parameters (optional)
455 * @return mixed WP_Error | object
456 */
457 private function request( $cmd, $method = 'get', $params = array() ) {
458
459 // Check required parameters exist
460 if ( empty( $this->access_token ) ) {
461 return new WP_Error( 'missing_access_token', __( 'No access token was specified', 'wp-to-social-pro' ) );
462 }
463
464 // Add access token to command, depending on the command's format
465 if ( strpos ( $cmd, '?' ) !== false ) {
466 $cmd .= '&access_token=' . $this->access_token;
467 } else {
468 $cmd .= '?access_token=' . $this->access_token;
469 }
470
471 // Build endpoint URL
472 $url = $this->api_endpoint . $this->api_version . '/' . $cmd;
473
474 // Define the timeout
475 $timeout = 20;
476
477 /**
478 * Defines the number of seconds before timing out a request to the Buffer API.
479 *
480 * @since 3.0.0
481 *
482 * @param int $timeout Timeout, in seconds
483 */
484 $timeout = apply_filters( $this->base->plugin->name . '_buffer_api_request', $timeout );
485
486 // Request via WordPress functions
487 $result = $this->request_wordpress( $url, $method, $params, $timeout );
488
489 // Request via cURL if WordPress functions failed
490 if ( defined( 'WP_DEBUG' ) && WP_DEBUG === true ) {
491 if ( is_wp_error( $result ) ) {
492 $result = $this->request_curl( $url, $method, $params, $timeout );
493 }
494 }
495
496 // Result will be WP_Error or the data we expect
497 return $result;
498
499 }
500
501 /**
502 * Performs POST and GET requests through WordPress wp_remote_post() and
503 * wp_remote_get() functions
504 *
505 * @since 3.2.6
506 *
507 * @param string $url URL
508 * @param string $method Method (post|get)
509 * @param array $params Parameters
510 * @param int $timeout Timeout, in seconds (default: 10)
511 * @return mixed WP_Error | object
512 */
513 private function request_wordpress( $url, $method, $params, $timeout = 20 ) {
514
515 // If proxy is enabled, send the request to our proxy with the URL, method and parameters
516 if ( $this->base->get_class( 'settings' )->get_option( 'proxy', false ) ) {
517 $response = wp_remote_get( $this->proxy_endpoint, array(
518 'body' => array(
519 'url' => $url,
520 'method' => $method,
521 'params' => http_build_query( $params ),
522 )
523 ) );
524 } else {
525 // Send request
526 switch ( $method ) {
527 /**
528 * GET
529 */
530 case 'get':
531 $response = wp_remote_get( $url, array(
532 'body' => $params,
533 'timeout' => $timeout,
534 ) );
535 break;
536
537 /**
538 * POST
539 */
540 case 'post':
541 $response = wp_remote_post( $url, array(
542 'body' => $params,
543 'timeout' => $timeout,
544 ) );
545 break;
546 }
547 }
548
549
550
551 // If an error occured, return it now
552 if ( is_wp_error( $response ) ) {
553 return $response;
554 }
555
556 // Fetch HTTP code and body
557 $http_code = wp_remote_retrieve_response_code( $response );
558 $response = wp_remote_retrieve_body( $response );
559
560 // Parse the response, to return the JSON data or an WP_Error object
561 return $this->parse_response( $response, $http_code, $params );
562
563 }
564
565 /**
566 * Performs POST and GET requests through PHP's curl_exec() function.
567 *
568 * If this function is called, request_wordpress() failed, most likely
569 * due to a DNS lookup failure or CloudFlare failing to respond.
570 *
571 * We therefore use CURLOPT_RESOLVE, to tell cURL the IP address for the domain.
572 *
573 * @since 3.2.6
574 *
575 * @param string $url URL
576 * @param string $method Method (post|get)
577 * @param array $params Parameters
578 * @param int $timeout Timeout, in seconds (default: 20)
579 * @return mixed WP_Error | object
580 */
581 private function request_curl( $url, $method, $params, $timeout = 20 ) {
582
583 // Bail if cURL isn't installed
584 if ( ! function_exists( 'curl_init' ) ) {
585 return new WP_Error(
586 $this->base->plugin->name . '_api_request_curl',
587 sprintf(
588 /* translators: Plugin Name */
589 __( '%s requires the PHP cURL extension to be installed and enabled by your web host.', 'wp-to-social-pro' ),
590 $this->base->plugin->displayName
591 )
592 );
593 }
594
595 // Init
596 $ch = curl_init();
597
598 // If proxy is enabled, send the request to our proxy with the URL, method and parameters
599 if ( $this->base->get_class( 'settings' )->get_option( 'proxy', false ) ) {
600 curl_setopt_array( $ch, array(
601 CURLOPT_URL => $this->proxy_endpoint . '?' . http_build_query( array(
602 'url' => $url,
603 'method' => $method,
604 'params' => http_build_query( $params ),
605 ) )
606 ) );
607 } else {
608 // Set request specific options
609 switch ( $method ) {
610 /**
611 * GET
612 */
613 case 'get':
614 curl_setopt_array( $ch, array(
615 CURLOPT_URL => $url . '&' . http_build_query( $params ),
616 CURLOPT_RESOLVE => array(
617 str_replace( 'https://', '', $this->api_endpoint ) . ':443:104.16.97.40',
618 str_replace( 'https://', '', $this->api_endpoint ) . ':443:104.16.98.40',
619 ),
620 ) );
621 break;
622
623 /**
624 * POST
625 */
626 case 'post':
627 curl_setopt_array( $ch, array(
628 CURLOPT_URL => $url,
629 CURLOPT_POST => true,
630 CURLOPT_POSTFIELDS => http_build_query( $params ),
631 CURLOPT_RESOLVE => array(
632 str_replace( 'https://', '', $this->api_endpoint ) . ':443:104.16.97.40',
633 str_replace( 'https://', '', $this->api_endpoint ) . ':443:104.16.98.40',
634 ),
635 ) );
636 break;
637 }
638 }
639
640 // Set shared options
641 curl_setopt_array( $ch, array(
642 CURLOPT_RETURNTRANSFER => true,
643 CURLOPT_HEADER => false,
644 CURLOPT_FOLLOWLOCATION => true,
645 CURLOPT_MAXREDIRS => 10,
646 CURLOPT_CONNECTTIMEOUT => $timeout,
647 CURLOPT_TIMEOUT => $timeout,
648 ) );
649
650 // Execute
651 $response = curl_exec( $ch );
652 $http_code = curl_getinfo( $ch, CURLINFO_HTTP_CODE );
653 $error = curl_error( $ch );
654 curl_close( $ch );
655
656 // If our error string isn't empty, something went wrong
657 if ( ! empty( $error ) ) {
658 return new WP_Error( $this->base->plugin->name . '_api_request_curl', $error );
659 }
660
661 // Parse the response, to return the JSON data or an WP_Error object
662 return $this->parse_response( $response, $http_code, $params );
663
664 }
665
666 /**
667 * Parses the response body and HTTP code, returning either
668 * a WP_Error object or the JSON decoded response body
669 *
670 * @since 3.9.8
671 *
672 * @param string $response Response Body
673 * @param int $http_code HTTP Code
674 * @param array $params Request Parameters
675 * @return mixed WP_Error | object
676 */
677 private function parse_response( $response, $http_code, $params ) {
678
679 // Decode response
680 $body = json_decode( $response );
681
682 // Return body if HTTP code is 200
683 if ( $http_code == 200 ) {
684 return $body;
685 }
686
687 // Return basic WP_Error if we don't have any more information
688 if ( is_null( $body ) ) {
689 return new WP_Error(
690 $http_code,
691 sprintf(
692 /* translators: HTTP Response Code */
693 __( 'Buffer API Error: HTTP Code %s. Sorry, we don\'t have any more information about this error. Please try again.', 'wp-to-social-pro' ),
694 $http_code
695 )
696 );
697 }
698
699 // Return detailed WP_Error
700 // Define the error message
701 $message = array();
702 if ( isset( $body->error ) ) {
703 $message[] = $body->error;
704 }
705 if ( isset( $body->message ) ) {
706 $message[] = $body->message;
707 }
708
709 // For certain error codes, we can provide better error messages to the user, detailing
710 // the steps they should take to resolve the issue.
711 switch ( $body->code ) {
712
713 /**
714 * Unauthorized
715 * Permission Denied
716 * Access Token Required
717 */
718 case 401:
719 case 403:
720 case 1001:
721 $message[] = __( 'Click the "Deauthorize Plugin" button, and then the "Authorize Plugin" button on the Plugin\'s Settings screen', 'wp-to-social-pro' );
722 break;
723
724 /**
725 * Parameter not recognized (invalid image url parameter supplied)
726 */
727 case 1003:
728 $message[] = __( 'Run this Plugin on a publicly accessible domain that does not have password protection.', 'wp-to-social-pro' );
729 break;
730
731 /**
732 * Featured Image Missing
733 * Message too long
734 */
735 case 1004:
736 if ( strpos( $body->message, 'image' ) !== false ) {
737 // If the site isn't web accessible, Buffer won't be able to fetch the image, even if it's specified
738 if ( $this->is_local_host() ) {
739 $message = array(
740 sprintf(
741 /* translators: Image URL */
742 __( 'Buffer can\'t fetch the image %s because your site is running on a local host and not web accessible. Please run the Plugin on a publicly accessible domain.', 'wp-to-social-pro' ),
743 $params['media']['picture']
744 )
745 );
746 } else {
747 $message = array(
748 __( 'A Featured Image is required for this status to be sent.', 'wp-to-social-pro' )
749 );
750 }
751 }
752 break;
753
754 /**
755 * No authorization to access profile
756 */
757 case 1011:
758 $message[] = sprintf(
759 /* translators: %1$s: Social Media Account Service/Type (e.g. Facebook, Twitter), %2$s: Social Media Account Name */
760 __( 'Pinterest: Choose a Pinterest board in the status settings. Otherwise, reconnect the %1$s Account %2$s in Buffer.', 'wp-to-social-pro' ),
761 $profile['formatted_service'],
762 $profile['formatted_username'],
763 'https://faq.buffer.com/article/294-publish-reconnect-social-account'
764 );
765 break;
766
767 /**
768 * Duplicate update
769 */
770 case 1025:
771 $message[] = __( 'Change the status text using the Per-Post Settings, to ensure it is slightly different from the last status.', 'wp-to-social-pro' );
772 break;
773
774 /**
775 * Media filetype not supported (...)
776 * The provided image does not appear to be valid i.e. is a localhost URL or invalid dimensions
777 * Whoops, so sorry! It looks like we had some trouble with your Facebook Page mentions. Would you be up for trying again?
778 */
779 case 1030:
780 if ( strpos( $body->message, 'Facebook Page mentions' ) !== false ) {
781 $message = array(
782 $body->message,
783 );
784 }
785
786 if ( strpos( $body->message, 'image' ) !== false ) {
787 if ( $this->is_local_host() ) {
788 $message = array(
789 sprintf(
790 __( 'Buffer can\'t fetch the image %s because your site is running on a local host and not web accessible. Please run the Plugin on a publicly accessible domain.', 'wp-to-social-pro' ),
791 ( isset( $params['media']['picture'] ) ? $params['media']['picture'] : '' )
792 )
793 );
794 } elseif ( strpos( $params['media']['picture'], '.webp' ) !== false ) {
795 $message = array(
796 sprintf(
797 /* translators: Image URL */
798 __( 'Buffer can\'t use the image %s, because WebP images are unsupported by Buffer. The Plugin attempted to convert this image to a supported format, but failed. Consider using a JPEG image instead.', 'wp-to-social-pro' ),
799 ( isset( $params['media']['picture'] ) ? $params['media']['picture'] : '' )
800 )
801 );
802 } else {
803 $message = array(
804 sprintf(
805 /* translators: %1$s: Image URL, %2$s: Link to Media File Renamer Plugin */
806 __( 'Buffer can\'t fetch the image %1$s. Install %2$s to automatically remove spaces, accented or special characters in image filenames.', 'wp-to-social-pro' ),
807 ( isset( $params['media']['picture'] ) ? $params['media']['picture'] : '' ),
808 '<a href="https://wordpress.org/plugins/media-file-renamer/" target="_blank">' . __( 'Media File Renamer Plugin', 'wp-to-social-pro' ) . '</a>'
809 )
810 );
811 }
812 }
813 break;
814
815 /**
816 * Cannot schedule updates in the past
817 */
818 case 1034:
819 if ( isset( $params['scheduled_at'] ) ) {
820 $message[] = sprintf(
821 /* translators: Scheduled Date and Time */
822 __( 'The Custom Time (based on Custom Field / Post Meta Value) field cannot be %s, which is a date in the past.', 'wp-to-social-pro' ),
823 $params['scheduled_at']
824 );
825 } else {
826 $message[] = sprintf(
827 /* translators: %1$s: Link to WordPress General Settings, %2$s: Link to Social Media Scheduling Timezone Settings Screen, %3$s: Social Media Scheduler Name (Buffer, Hootsuite, SocialPilot) */
828 __( '<a href="%1$s">WordPress</a> and <a href="%2$s">%3$s</a> timezones must match.', 'wp-to-social-pro' ),
829 admin_url( 'options-general.php' ),
830 $this->get_timezone_settings_url( $params['profile_ids'][0] ),
831 $this->base->plugin->account
832 );
833 }
834 break;
835
836 }
837
838 // Return WP_Error
839 return new WP_Error(
840 $body->code,
841 sprintf(
842 /* translators: %1$s: API Error Code, %2$s: API Error Message */
843 __( 'Buffer API Error: #%1$s: %2$s', 'wp-to-social-pro' ),
844 $body->code,
845 implode( "\n", $message )
846 )
847 );
848
849 }
850
851 /**
852 * Determines if the WordPress URL is a local, non-web accessible URL.
853 *
854 * @since 4.1.9
855 *
856 * @return bool Locally Hosted Site
857 */
858 private function is_local_host() {
859
860 // Get URL of site and its information
861 $url = parse_url( get_bloginfo( 'url' ) );
862
863 // Iterate through local host addresses to check if they exist
864 // in part of the site's URL host
865 foreach ( $this->get_local_hosts() as $local_host ) {
866 if ( strpos( $url['host'], $local_host ) !== false ) {
867 return true;
868 }
869 }
870
871 // If here, we're not on a local host
872 return false;
873
874 }
875
876 /**
877 * Returns an array of domains and IP addresses that are non-web accessible
878 *
879 * @since 4.1.9
880 *
881 * @return array Non-web accessible Domains and IP addresses
882 */
883 private function get_local_hosts() {
884
885 // If domain is 127.0.0.1, localhost or .dev, don't count it towards the domain limit
886 // The user has a valid license key if they're here, so that's enough
887 // See: https://www.sqa.org.uk/e-learning/WebTech01CD/page_12.htm
888 $local_hosts = array(
889 'localhost',
890 '127.0.0.1',
891 '10.0.',
892 '192.168.',
893 '.dev',
894 '.local',
895 '.localhost',
896 '.test',
897 );
898
899 // Add 172.16.0.* to 172.16.31.*
900 for ( $i = 0; $i <= 31; $i++ ) {
901 $local_hosts[] = '172.16.' . $i . '.';
902 }
903
904 return $local_hosts;
905
906 }
907
908 }