PluginProbe ʕ •ᴥ•ʔ
Reviews Feed – Add Testimonials and Customer Reviews From Google Reviews, Yelp, TripAdvisor, and More / 2.11.0
Reviews Feed – Add Testimonials and Customer Reviews From Google Reviews, Yelp, TripAdvisor, and More v2.11.0
2.11.0 2.10.0 2.9.0 2.8.0 2.7.0 2.6.7 2.6.8 2.6.5 2.6.4 2.6.3 2.6.2 2.6.0 2.5.5 2.5.4 2.5.3 2.5.2 trunk 1.0 1.0.1 1.0.2 1.0.3 1.1 1.1.1 1.1.2 1.2.0 2.0 2.1.0 2.1.1 2.4.0 2.4.1 2.4.2 2.4.3 2.4.4 2.4.5 2.4.6 2.5.0 2.5.1
reviews-feed / class / Common / Error_Reporter.php
reviews-feed / class / Common Last commit date
Admin 1 week ago Builder 1 week ago Customizer 1 week ago Exceptions 1 week ago Helpers 1 week ago Integrations 1 week ago Migrations 1 week ago ReviewAlerts 1 week ago Services 1 week ago Settings 1 week ago Support 1 week ago Traits 1 week ago UsageTracking 1 week ago Utils 1 week ago AuthorizationStatusCheck.php 1 week ago BusinessDataCache.php 1 week ago Clear_Cache.php 1 week ago Container.php 1 week ago DisplayElements.php 1 week ago Email_Notification.php 1 week ago Error_Reporter.php 1 week ago Feed.php 1 week ago FeedCache.php 1 week ago FeedCacheUpdater.php 1 week ago FeedDisplay.php 1 week ago Feed_Locator.php 1 week ago Parser.php 1 week ago PostAggregator.php 1 week ago RemoteRequest.php 1 week ago SBR_Education.php 1 week ago SBR_Schema_Service.php 1 week ago SBR_Settings.php 1 week ago ServiceContainer.php 1 week ago SinglePostCache.php 1 week ago TemplateRenderer.php 1 week ago Tooltip_Wizard.php 1 week ago Util.php 1 week ago
Error_Reporter.php
582 lines
1 <?php
2
3 /**
4 * Class CFF_Error_Reporter
5 *
6 * Set as a global object to record and report errors
7 *
8 * @since X.X
9 */
10
11 namespace SmashBalloon\Reviews\Common;
12
13 use SmashBalloon\Reviews\Common\Builder\SBR_Sources;
14 use SmashBalloon\Reviews\Common\Customizer\DB;
15 use Smashballoon\Stubs\Services\ServiceProvider;
16
17 //Exit if accessed directly
18 if (!defined('ABSPATH')) {
19 exit;
20 }
21
22 class Error_Reporter extends ServiceProvider
23 {
24 /**
25 * @var array
26 */
27 public $errors;
28
29 /**
30 * @var array
31 */
32 public $frontend_error;
33
34 /**
35 * @var string
36 */
37 public $reporter_key;
38
39 /**
40 * @var array
41 */
42 public $display_error;
43
44
45 /**
46 * CFF_Error_Reporter constructor.
47 */
48 public function __construct()
49 {
50 $this->reporter_key = 'sbr_error_reporter';
51 $this->errors = get_option($this->reporter_key, []);
52 if (!isset($this->errors['connection'])) {
53 $this->errors = array(
54 'connection' => [],
55 'resizing' => [],
56 'database_create' => [],
57 'upload_dir' => [],
58 'accounts' => [],
59 'error_log' => [],
60 'action_log' => [],
61 'revoked' => []
62 );
63 }
64
65
66 $this->display_error = [];
67 $this->frontend_error = '';
68 }
69
70 public function register()
71 {
72 add_action('sbr_feed_issue_email', [$this, 'maybe_trigger_report_email_send']);
73 add_action('wp_ajax_sbr_dismiss_critical_notice', [$this, 'dismiss_critical_notice']);
74 add_action('wp_footer', [$this, 'critical_error_notice'], 300);
75 }
76
77 /**
78 * @return array
79 *
80 * @since X.X
81 */
82 public function get_errors()
83 {
84 return $this->errors;
85 }
86
87 /**
88 * @param $type
89 * @param $message_array
90 *
91 * @since X>X
92 */
93 public function add_error($type, $args, $connected_account_term = false)
94 {
95 $connected_account = false;
96 $log_item = date('m-d H:i:s') . ' - ';
97
98 if ($connected_account_term !== false) {
99 if (!is_array($connected_account_term)) {
100 $connected_account = SBR_Sources::get_single_source_info(
101 [
102 'id' => $connected_account_term,
103 'provider' => 'facebook'
104 ]
105 );
106 } else {
107 $connected_account = $connected_account_term;
108 }
109 $this->add_connected_account_error($connected_account, $type, $args);
110 }
111
112 //Access Token Error
113 if ($type === 'accesstoken') {
114 $accesstoken_error_exists = false;
115 if (isset($this->errors['accounts'])) {
116 foreach ($this->errors['accounts'] as $account) {
117 if ($args['accesstoken'] === $account['accesstoken']) {
118 $accesstoken_error_exists = true;
119 }
120 }
121 }
122 if (!$accesstoken_error_exists && isset($this->errors['accounts'])) {
123 $this->errors['accounts'][$connected_account['id']][] = array(
124 'accesstoken' => $args['accesstoken'],
125 'post_id' => $args['post_id'],
126 'critical' => true,
127 'type' => $type,
128 'errorno' => $args['errorno']
129 );
130 }
131 }
132
133 //Connection Error API & WP REMOTE CALL
134 if ($type === 'api' || $type === 'wp_remote_get') {
135 $connection_details = array(
136 'error_id' => ''
137 );
138 $connection_details['critical'] = false;
139
140 if (isset($args['error']['code'])) {
141 $connection_details['error_id'] = $args['error']['code'];
142 if ($this->is_critical_error($args)) {
143 $connection_details['critical'] = true;
144 }
145
146 if ($this->is_app_permission_related($args)) {
147 if (!isset($this->errors['revoked']) || (!is_array($this->errors['revoked']))) {
148 $this->errors['revoked'] = array();
149 }
150 if (isset($connected_account['account_id']) && !in_array($connected_account['account_id'], $this->errors['revoked'], true)) {
151 $this->errors['revoked'][] = $connected_account['account_id'];
152 }
153 do_action('sbr_app_permission_revoked', $connected_account);
154 }
155 } elseif (isset($args['response']) && is_wp_error($args['response'])) {
156 foreach ($args['response']->errors as $key => $item) {
157 $connection_details['error_id'] = $key;
158 }
159 $connection_details['critical'] = true;
160 }
161
162 $connection_details['error_message'] = $this->generate_error_message($args, $connected_account);
163 $log_item .= $connection_details['error_message']['admin_message'];
164 $this->errors['connection'] = $connection_details;
165 }
166
167 if ($type === 'platform_data_deleted') {
168 $this->errors['platform_data_deleted'] = $args[0];
169 $log_item .= is_array($args) ? wp_json_encode($args) : $args;
170 }
171
172 $current_log = $this->errors['error_log'];
173 if (is_array($current_log) && count($current_log) >= 10) {
174 reset($current_log);
175 unset($current_log[key($current_log)]);
176 }
177 $current_log[] = $log_item;
178 $this->errors['error_log'] = $current_log;
179 update_option($this->reporter_key, $this->errors, false);
180 }
181
182 /**
183 * Stores information about an encountered error related to a connected account
184 *
185 * @param $connected_account array
186 * @param $error_type string
187 * @param $details mixed/array/string
188 *
189 * @since X.X.X
190 */
191 public function add_connected_account_error($connected_account, $error_type, $details)
192 {
193 $account_id = $connected_account['id'];
194 $this->errors['accounts'][$account_id][$error_type] = $details;
195
196 if ($error_type === 'api' || $error_type === 'accesstoken') {
197 $this->errors['accounts'][$account_id][$error_type]['clear_time'] = time() + 60 * 3;
198 }
199
200 if (isset($details['error']['code']) && (int)$details['error']['code'] === 18) {
201 $this->errors['accounts'][$account_id][$error_type]['clear_time'] = time() + 60 * 15;
202 }
203 //\CustomFacebookFeed\Builder\CFF_Source::add_error($account_id, $details);
204 }
205
206 /**
207 * @return mixed
208 *
209 * @since X.X.X
210 */
211 public function get_error_log()
212 {
213 return $this->errors['error_log'];
214 }
215
216 /**
217 * Certain API errors are considered critical and will trigger
218 * the various notifications to users to correct them.
219 *
220 * @param $details
221 *
222 * @return bool
223 *
224 * @since X.X
225 */
226 public function is_critical_error($details)
227 {
228 $error_code = (int)$details['error']['code'];
229 $critical_codes = array(
230 10,
231 100,
232 200,
233 190,
234 104,
235 999
236 );
237 return in_array($error_code, $critical_codes, true);
238 }
239
240 /**
241 * Removes Single Error
242 *
243 * @param $type
244 *
245 * @since X.X.X
246 */
247 public function remove_error($type, $connected_account = false)
248 {
249 $update = false;
250 if (!empty($this->errors[$type])) {
251 $this->errors[$type] = array();
252 $this->add_action_log('Cleared ' . $type . ' error.');
253 $update = true;
254 }
255
256 if (!empty($this->errors['revoked'])) {
257 if (!is_array($this->errors['revoked'])) {
258 $this->errors['revoked'] = array();
259 }
260 if (isset($connected_account['account_id']) && ($key = array_search($connected_account['account_id'], $this->errors['revoked'])) !== false) {
261 unset($this->errors['revoked'][$key]);
262 }
263 }
264
265 if ($update) {
266 update_option($this->reporter_key, $this->errors, false);
267 }
268 }
269
270 /**
271 * Resets and Removes All Errors
272 *
273 * @since X.X.X
274 */
275 public function remove_all_errors()
276 {
277 delete_option($this->reporter_key);
278 }
279
280 /**
281 * Resets and Removes All API Errors
282 *
283 * @since X.X.X
284 */
285 public function reset_api_errors()
286 {
287 $this->errors['connection'] = array();
288 $this->errors['accounts'] = array();
289 update_option($this->reporter_key, $this->errors, false);
290 }
291
292 /**
293 * Stores a time stamped string of information about
294 * actions that might lead to correcting an error
295 *
296 * @param string $log_item
297 *
298 * @since X.X.X
299 */
300 public function add_action_log($log_item)
301 {
302 $current_log = $this->errors['action_log'];
303
304 if (is_array($current_log) && count($current_log) >= 10) {
305 reset($current_log);
306 unset($current_log[key($current_log)]);
307 }
308 $current_log[] = date('m-d H:i:s') . ' - ' . $log_item;
309
310 $this->errors['action_log'] = $current_log;
311 update_option($this->reporter_key, $this->errors, false);
312 }
313
314 /**
315 * Get Action Logs
316 *
317 * @return mixed
318 *
319 * @since X.X.X
320 */
321 public function get_action_log()
322 {
323 return $this->errors['action_log'];
324 }
325
326 /**
327 * Should clear platform data
328 *
329 * @param $details
330 *
331 * @return bool
332 *
333 * @since X.X
334 */
335 public function is_app_permission_related($details)
336 {
337 $error_code = (int)$details['error']['code'];
338 $critical_codes = array(
339 190, // access token or permissions
340 );
341 return in_array($error_code, $critical_codes, true) && strpos($details['error']['message'], 'user has not authorized application') !== false;
342 }
343
344 public function maybe_trigger_report_email_send()
345 {
346 if (!$this->are_critical_errors()) {
347 return;
348 }
349 /** TODO: Match real option */
350 $options = get_option('sbr_settings', array());
351 if (! is_array($options)) {
352 $options = array();
353 }
354
355 if (isset($options['enable_email_report']) && empty($options['enable_email_report'])) {
356 return;
357 }
358 $this->send_report_email();
359 }
360
361 /**
362 * Whether or not there was a platform data clearing error
363 *
364 * @return bool
365 */
366 public function was_app_permission_related_error()
367 {
368 return !empty($this->errors['revoked']);
369 }
370
371 /**
372 * App Permission Errors
373 *
374 * @return array
375 */
376 public function get_app_permission_related_error_ids()
377 {
378 return $this->errors['revoked'];
379 }
380
381 /**
382 * Are Critical Errors
383 *
384 * @return bool
385 */
386 public function are_critical_errors()
387 {
388 $are_errors = false;
389 $errors = $this->get_errors();
390 if (isset($errors['connection']['critical']) && $errors['connection']['critical'] === true) {
391 return true;
392 } else {
393 $connected_accounts = DB::get_facebook_sources();
394
395 foreach ($connected_accounts as $connected_account) {
396 $connected_account = (array)$connected_account;
397
398 if (isset($connected_account['account_id']) && isset($this->errors['accounts'][$connected_account['account_id']]['api'])) {
399 if (isset($this->errors['accounts'][$connected_account['account_id']]['api']['error'])) {
400 return $this->is_critical_error($this->errors['accounts'][$connected_account['account_id'] ]['api']);
401 }
402 }
403 }
404 }
405 return $are_errors;
406 }
407
408 /**
409 * Creates an array of information for easy display of API errors
410 *
411 * @param $response
412 * @param array $connected_account
413 *
414 * @return array
415 *
416 * @since X.X.X
417 */
418 public function generate_error_message($response, $connected_account = array('username' => ''))
419 {
420 $error_message_return = array(
421 'public_message' => '',
422 'admin_message' => '',
423 'frontend_directions' => '',
424 'backend_directions' => '',
425 'post_id' => get_the_ID(),
426 'errorno' => '',
427 'time' => time()
428 );
429
430 if (isset($response['error']['code'])) {
431 $error_code = (int)$response['error']['code'];
432 if ($error_code === 104) {
433 $error_code = 999;
434 $url = 'https://smashballoon.com/doc/error-999-access-token-could-not-be-decrypted/?utm_campaign=reviews-free&utm_source=error&utm_medium=docs';
435
436 $response['error']['message'] = __('Your access token could not be decrypted on this website. Reconnect this account or go to our website to learn how to prevent this.', 'reviews-feed');
437 } else {
438 $url = 'https://smashballoon.com/doc/facebook-api-errors/?utm_campaign=reviews-free&utm_source=error&utm_medium=docs';
439 }
440 $api_error_number_message = sprintf(__('API Error %s:', 'reviews-feed'), $error_code);
441 $error_message_return['public_message'] = __('Error connecting to the Facebook API.', 'reviews-feed') . ' ' . $api_error_number_message;
442 $ppca_error = (strpos($response['error']['message'], 'Public Content Access') !== false) ? true : false;
443
444 $error_message_return['admin_message'] = ($ppca_error)
445 ? '<B>PPCA Error:</b> Due to Facebook API changes it is no longer possible to display a feed from a Facebook Page you are not an admin of. Please use the button below for more information on how to fix this.'
446 : '<strong>' . $api_error_number_message . '</strong><br>' . $response['error']['message'];
447
448 $error_message_return['frontend_directions'] = ($ppca_error)
449 ? '<p class="sbr-error-directions"><a href="https://smashballoon.com/facebook-api-changes-september-4-2020/?utm_campaign=reviews-free&utm_source=error&utm_medium=docs" target="_blank" rel="noopener">' . __('Directions on How to Resolve This Issue', 'reviews-feed') . '</a></p>'
450 : '<p class="sbr-error-directions"><a href="' . $url . '&facebook#' . absint($error_code) . '" target="_blank" rel="noopener">' . __('Directions on How to Resolve This Issue', 'reviews-feed') . '</a></p>';
451
452 $error_message_return['backend_directions'] = ($ppca_error)
453 ? '<a class="sbr-notice-btn sbr-btn-blue" href="https://smashballoon.com/facebook-api-changes-september-4-2020/?utm_campaign=reviews-free&utm_source=error&utm_medium=docs" target="_blank" rel="noopener">' . __('Directions on How to Resolve This Issue', 'reviews-feed') . '</a>'
454 : '<a class="sbr-notice-btn sbr-btn-blue" href="' . $url . '&facebook#' . absint($error_code) . '" target="_blank" rel="noopener">' . __('Directions on How to Resolve This Issue', 'reviews-feed') . '</a>';
455
456 $error_message_return['errorno'] = $error_code;
457 } else {
458 $error_message_return['error_message'] = __('An unknown error has occurred.', 'reviews-feed');
459 $error_message_return['admin_message'] = wp_json_encode($response);
460 }
461 return $error_message_return;
462 }
463
464
465 /**
466 * Send Email Repport
467 *
468 *
469 * @since X.X.X
470 */
471 public function send_report_email()
472 {
473 $options = get_option('sbr_settings', array());
474 if (! is_array($options)) {
475 $options = array();
476 }
477
478 $to_string = ! empty($options['email_notification_addresses']) ? str_replace(' ', '', $options['email_notification_addresses']) : get_option('admin_email', '');
479
480 $to_array_raw = explode(',', $to_string);
481 $to_array = array();
482
483 foreach ($to_array_raw as $email) {
484 if (is_email($email)) {
485 $to_array[] = $email;
486 }
487 }
488
489 if (empty($to_array)) {
490 return false;
491 }
492
493 $headers = array('Content-Type: text/html; charset=utf-8');
494
495 $header_image = SBR_PLUGIN_URL . '/assets/images/balloon-120.png';
496
497 $title = __('Reviews Feed Report for ' . home_url());
498 $link = admin_url('admin.php?page=sbr-settings');
499 //&tab=customize-advanced
500 $footer_link = admin_url('admin.php?page=sbr-style&tab=misc&flag=emails');
501 $bold = __('There\'s an Issue with a Reviews Feed on Your Website', 'reviews-feed');
502 $details = '<p>' . __('A Custom Reviews Feed on your website is currently unable to connect to Facebook to retrieve new posts. Don\'t worry, your feed is still being displayed using a cached version, but is no longer able to display new posts.', 'reviews-feed') . '</p>';
503 $details .= '<p>' . sprintf(__('This is caused by an issue with your Facebook account connecting to the Facebook API. For information on the exact issue and directions on how to resolve it, please visit the %sCustom Facebook Feed settings page%s on your website.', 'reviews-feed'), '<a href="' . esc_url($link) . '">', '</a>') . '</p>';
504 $message_content = '<h6 style="padding:0;word-wrap:normal;font-family:\'Helvetica Neue\',Helvetica,Arial,sans-serif;font-weight:bold;line-height:130%;font-size: 16px;color:#444444;text-align:inherit;margin:0 0 20px 0;Margin:0 0 20px 0;">' . $bold . '</h6>' . $details;
505
506 $educator = new SBR_Education();
507 $dyk_message = $educator->dyk_display();
508
509 ob_start();
510 include_once SBR_PLUGIN_URL . '/templates/email.php';
511 $email_body = ob_get_contents();
512 ob_get_clean();
513 $sent = wp_mail($to_array, $title, $email_body, $headers);
514 return $sent;
515 }
516
517
518 /**
519 * Returns Array of Notices
520 *
521 * @return array
522 *
523 * @since X.X.X
524 */
525 public function get_facebook_platform_notices()
526 {
527 $errors = $this->get_errors();
528 $notices = [];
529 if (!empty($errors) && (!empty($errors['platform_data_deleted']))) {
530 $notices[] = [
531 'type' => 'error',
532 'heading' => __('All Facebook Data has Been Remove.', 'reviews-feed'),
533 'description' => __('An account admin has deauthorized the Smash Balloon app used to power the Facebook Feed plugin. The page was not reconnected within the 7 day limit and all Facebook data was automatically deleted on your website due to Facebook data privacy rules.<br/><br/>Due to API limitations your feeds will not show new reviews until you enter an API key on the settings page.', 'reviews-feed'),
534 ];
535 }
536
537 if (!empty($errors) && (!empty($errors['unused_feed']))) {
538 $notices[] = [
539 'type' => 'error',
540 'heading' => __('Action Required Within 7 Days:', 'reviews-feed'),
541 'description' => __('Your Facebook Reviews feed has been not viewed in the last 14 days. Due to Facebook data privacy rules, all data for this feed will be deleted in 7 days time. To avoid automated data deletion, simply view the Facebook feed on your website within the next 7 days.', 'reviews-feed'),
542 ];
543 }
544
545 if (!empty($errors) && isset($errors['connection']['error_id']) && isset($errors['connection']['error_message']['admin_message']) && $errors['connection']['error_id'] === 190 && strpos($errors['connection']['error_message']['admin_message'], 'user has not authorized application') !== false) {
546 $accounts_revoked = '';
547 if ($this->was_app_permission_related_error()) {
548 $accounts_revoked = $this->get_app_permission_related_error_ids();
549 if (count($accounts_revoked) > 1) {
550 $accounts_revoked = implode(', ', $accounts_revoked);
551 } else {
552 $accounts_revoked = $accounts_revoked[0];
553 }
554 }
555
556 $notices[] = [
557 'type' => 'error',
558 'heading' => __('Action Required Within 7 Days', 'reviews-feed'),
559 'description' => [
560 __('An account admin has deauthorized the Smash Balloon app used to power the Facebook Feed plugin.', 'reviews-feed'),
561 sprintf(
562 __('Facebook Feed related data for the account(s) %s was removed due to permission for the Smash Balloon App on Facebook being revoked.', 'reviews-feed'),
563 $accounts_revoked
564 ),
565 __('To prevent the automated data deletion for the account, please reconnect your account within 7 days.', 'reviews-feed'),
566 '<a href="https://smashballoon.com/doc/action-required-within-7-days/?reviews&utm_campaign=reviews-pro&utm_source=permissionerror&utm_medium=notice&utm_content=More Information" target="_blank" rel="noopener">' . __('More Information', 'reviews-feed') . '</a>',
567 ]
568 ];
569 }
570
571 return $notices;
572 }
573
574 /**
575 * Load the critical notice for logged in users.
576 */
577 public function critical_error_notice()
578 {
579 }
580
581 }
582