PluginProbe ʕ •ᴥ•ʔ
Smash Balloon Social Post Feed – Simple Social Feeds for WordPress / 4.12.0
Smash Balloon Social Post Feed – Simple Social Feeds for WordPress v4.12.0
4.12.0 4.10.0 4.9.0 4.8.1 trunk 1.0 1.1 1.12.1 1.2.3 1.2.4 1.2.5 1.2.7 1.2.8 1.2.9 1.3.0 1.3.1 1.3.2 1.3.3 1.3.4 1.3.5 1.3.6 1.3.7 1.4.0 1.4.1 1.4.2 1.4.3 1.4.4 1.4.5 1.4.6 1.4.7 1.4.8 1.5 1.5.1 1.5.2 1.5.9 1.6.0 1.6.1 1.6.2 1.6.3 1.6.4 1.6.4.1 1.6.5 1.6.5.1 1.6.6 1.6.6.1 1.6.6.2 1.6.6.3 1.6.7 1.6.7.1 1.6.8 1.6.8.1 1.6.8.2 1.7.0 1.7.1 1.7.2 1.8.0 1.8.1 1.8.2 1.8.2.1 1.8.2.2 1.8.2.3 1.9.0 1.9.1 1.9.3 1.9.4 1.9.5 1.9.6 1.9.7 1.9.8 1.9.8.1 1.9.9 1.9.9.1 1.9.9.2 1.9.9.3 2.0 2.0.1 2.1 2.1.1 2.1.2 2.1.3 2.10 2.11 2.11.1 2.12 2.12.1 2.12.2 2.12.3 2.12.4 2.13 2.14 2.14.1 2.15 2.15.1 2.16 2.16.1 2.17 2.17.1 2.18 2.18.1 2.18.2 2.18.3 2.19 2.19.1 2.19.2 2.19.3 2.2 2.2.1 2.3 2.3.1 2.3.10 2.3.2 2.3.3 2.3.4 2.3.6 2.3.7 2.3.8 2.3.9 2.4 2.4.1 2.4.1.1 2.4.1.2 2.4.2 2.4.3 2.4.4 2.4.5 2.4.6 2.5 2.5.1 2.5.2 2.6 2.6.1 2.6.2 2.6.3 2.6.4 2.7 2.7.1 2.7.2 2.8 2.9 2.9.1 4.0 4.0.1 4.0.2 4.0.3 4.0.4 4.0.5 4.1 4.1.1 4.1.2 4.1.3 4.1.4 4.1.5 4.1.6 4.1.7 4.1.8 4.1.9 4.2 4.2.1 4.2.2 4.2.3 4.2.4 4.2.5 4.2.6 4.3.0 4.3.1 4.3.2 4.3.3 4.3.4 4.7.5 4.7.6 4.7.7
custom-facebook-feed / inc / Builder / CFF_Source.php
custom-facebook-feed / inc / Builder Last commit date
Controls 2 weeks ago Tabs 2 weeks ago CFF_Builder_Customizer.php 2 weeks ago CFF_Db.php 2 weeks ago CFF_Feed_Builder.php 2 weeks ago CFF_Feed_Saver.php 2 weeks ago CFF_Feed_Saver_Manager.php 2 weeks ago CFF_Post_Set.php 2 weeks ago CFF_Source.php 2 weeks ago CFF_Theme_CSS.php 2 weeks ago CFF_Tooltip_Wizard.php 2 weeks ago index.php 2 weeks ago
CFF_Source.php
1172 lines
1 <?php
2
3 /**
4 * Custom Facebook Feed Source
5 *
6 * @since 4.0
7 */
8
9 namespace CustomFacebookFeed\Builder;
10
11 use CustomFacebookFeed\CFF_Response;
12 use CustomFacebookFeed\CFF_Utils;
13
14 if (!defined('ABSPATH')) {
15 exit; // Exit if accessed directly
16 }
17
18 class CFF_Source
19 {
20 const BATCH_SIZE = 15;
21
22 /**
23 * AJAX hooks for various feed data related functionality
24 *
25 * @since 4.0
26 */
27 public static function hooks()
28 {
29 add_action('wp_ajax_cff_source_builder_update', array( 'CustomFacebookFeed\Builder\CFF_Source', 'builder_update' ));
30 add_action('wp_ajax_cff_source_builder_update_multiple', array( 'CustomFacebookFeed\Builder\CFF_Source', 'builder_update_multiple' ));
31 add_action('wp_ajax_cff_source_get_page', array( 'CustomFacebookFeed\Builder\CFF_Source', 'get_page' ));
32 add_action('wp_ajax_cff_source_get_featured_post_preview', array( 'CustomFacebookFeed\Builder\CFF_Source', 'get_featured_post_preview' ));
33 add_action('wp_ajax_cff_source_get_playlist_post_preview', array( 'CustomFacebookFeed\Builder\CFF_Source', 'get_playlist_post_preview' ));
34 add_action('admin_init', array( 'CustomFacebookFeed\Builder\CFF_Source', 'batch_process_legacy_source_queue' ));
35 }
36
37 /**
38 * Used in an AJAX call to update sources based on selections or
39 * input from a user. Makes an API request to add additiona info
40 * about the connected source.
41 *
42 * @since 4.0
43 */
44 public static function builder_update()
45 {
46 $action = 'cff-admin';
47 if (! empty($_POST['settings_page'])) {
48 $action = 'cff_admin_nonce';
49 }
50 check_ajax_referer($action, 'nonce');
51
52 $cap = current_user_can('manage_custom_facebook_feed_options') ? 'manage_custom_facebook_feed_options' : 'manage_options';
53 $cap = apply_filters('cff_settings_pages_capability', $cap);
54 if (! current_user_can($cap)) {
55 wp_send_json_error(); // This auto-dies.
56 }
57
58
59 $source_data = array(
60 'access_token' => sanitize_text_field($_POST['access_token']),
61 'id' => sanitize_text_field($_POST['id']),
62 'type' => sanitize_text_field($_POST['type']),
63 'privilege' => isset($_POST['privilege']) ? sanitize_text_field($_POST['privilege']) : '',
64 );
65
66 if (! empty($_POST['name'])) {
67 $source_data['name'] = sanitize_text_field($_POST['name']);
68 }
69
70 $return = CFF_Source::process_connecting_source_data($source_data);
71
72 echo $return;
73
74 wp_die();
75 }
76
77
78 /**
79 * Add our update a source from raw API data.
80 *
81 * @param $source_data
82 *
83 * @return string
84 */
85 public static function process_connecting_source_data($source_data)
86 {
87 $header_details = \CustomFacebookFeed\CFF_Utils::fetch_header_data($source_data['id'], $source_data['type'] === 'group', $source_data['access_token'], 0, false, '');
88
89 if (! isset($header_details->name)) {
90 $message = __('There was a problem connecting this account. Please make sure your access token and ID are correct.');
91 $details = '';
92 if (isset($header_details->cached_error)) {
93 $details = $header_details->cached_error->message;
94 $details = '<span class="sb-caption">API Response: ' . esc_html($details) . '</span>';
95 } elseif (isset($header_details->error)) {
96 $details = $header_details->error->message;
97 $details = '<span class="sb-caption">API Response: ' . esc_html($details) . '</span>';
98 }
99
100 $return_html = '<div class="cff-groups-connect-actions sb-alerts-wrap"><div class="sb-alert">
101 <svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
102 <path d="M8.99935 0.666504C4.39935 0.666504 0.666016 4.39984 0.666016 8.99984C0.666016 13.5998 4.39935 17.3332 8.99935 17.3332C13.5993 17.3332 17.3327 13.5998 17.3327 8.99984C17.3327 4.39984 13.5993 0.666504 8.99935 0.666504ZM9.83268 13.1665H8.16602V11.4998H9.83268V13.1665ZM9.83268 9.83317H8.16602V4.83317H9.83268V9.83317Z" fill="#995C00"/>
103 </svg>
104 <span><strong>' . esc_html($message) . '</strong></span><br>
105 ' . $details . '
106 </div></div>';
107
108 $return = array(
109 'success' => false,
110 'message' => $return_html
111 );
112 return \CustomFacebookFeed\CFF_Utils::cff_json_encode($return);
113 }
114
115 if (isset($header_details->shortcode_options)) {
116 unset($header_details->shortcode_options);
117 }
118
119 if (isset($header_details->name)) {
120 $source_data['name'] = $header_details->name;
121 }
122 $source_data['info'] = $header_details;
123
124 // don't update or insert the access token if there is an API error
125 if (! isset($header_details->error) && ! isset($header_details->cached_error)) {
126 $source_data['error'] = '';
127 $source_data['info']->connected_version = CFFVER;
128 CFF_Source::update_or_insert($source_data);
129 }
130
131 return \CustomFacebookFeed\CFF_Utils::cff_json_encode(CFF_Feed_Builder::get_source_list());
132 }
133
134 /**
135 * Used in an AJAX call to update Multiple sources based on selections or
136 * input from a user. Makes an API request to add additiona info
137 * about the connected source.
138 *
139 * @since 4.0
140 */
141 public static function builder_update_multiple()
142 {
143
144 if (check_ajax_referer('cff_admin_nonce', 'nonce', false) || check_ajax_referer('cff-admin', 'nonce', false)) {
145 $cap = current_user_can('manage_custom_facebook_feed_options') ? 'manage_custom_facebook_feed_options' : 'manage_options';
146 $cap = apply_filters('cff_settings_pages_capability', $cap);
147 if (! current_user_can($cap)) {
148 wp_send_json_error(); // This auto-dies.
149 }
150 $has_error = false;
151 if (isset($_POST['sourcesList']) && !empty($_POST['sourcesList']) && is_array($_POST['sourcesList'])) {
152 foreach ($_POST['sourcesList'] as $single_source) :
153 $source_data = array(
154 'access_token' => sanitize_text_field($single_source['access_token']),
155 'id' => sanitize_text_field($single_source['account_id']),
156 'name' => isset($single_source['name']) ? sanitize_text_field($single_source['name']) : '',
157 'type' => sanitize_text_field($_POST['type']),
158 'privilege' => isset($single_source['privilege']) ? sanitize_text_field($single_source['privilege']) : '',
159 );
160 $header_details = \CustomFacebookFeed\CFF_Utils::fetch_header_data($source_data['id'], $source_data['type'] === 'group', $source_data['access_token'], 0, false, '');
161 if (isset($header_details->shortcode_options)) {
162 unset($header_details->shortcode_options);
163 }
164 if (isset($header_details->name)) {
165 $source_data['name'] = $header_details->name;
166 }
167 $source_data['info'] = $header_details;
168 // don't update or insert the access token if there is an API error
169 if (! isset($header_details->error)) {
170 $source_data['error'] = '';
171 $source_data['info']->connected_version = CFFVER;
172 CFF_Source::update_or_insert($source_data);
173 } else {
174 $has_error = true;
175 }
176 endforeach;
177 }
178 $response = [
179 'sourcesList' => CFF_Feed_Builder::get_source_list()
180 ];
181 if ($has_error) {
182 $response['hasError'] = true;
183 }
184 echo \CustomFacebookFeed\CFF_Utils::cff_json_encode($response);
185 }
186
187 wp_die();
188 }
189
190 /**
191 * Get a list of sources with a limit and offset like a page
192 *
193 * @since 4.0
194 */
195 public static function get_page()
196 {
197 check_ajax_referer('cff-admin', 'nonce');
198
199 $cap = current_user_can('manage_custom_facebook_feed_options') ? 'manage_custom_facebook_feed_options' : 'manage_options';
200 $cap = apply_filters('cff_settings_pages_capability', $cap);
201 if (! current_user_can($cap)) {
202 wp_send_json_error(); // This auto-dies.
203 }
204
205
206 $args = array( 'page' => $_POST['page'] );
207 $source_data = CFF_Db::source_query($args);
208
209 echo \CustomFacebookFeed\CFF_Utils::cff_json_encode($source_data);
210
211 wp_die();
212 }
213
214 /**
215 * Using the URL and source ID, info about a single post is returned
216 *
217 * @since 4.0
218 */
219 public static function get_featured_post_preview()
220 {
221 check_ajax_referer('cff-admin', 'nonce');
222
223 $cap = current_user_can('manage_custom_facebook_feed_options') ? 'manage_custom_facebook_feed_options' : 'manage_options';
224 $cap = apply_filters('cff_settings_pages_capability', $cap);
225 if (! current_user_can($cap)) {
226 wp_send_json_error(); // This auto-dies.
227 }
228
229
230 $query_args = array(
231 'id' => sanitize_text_field($_POST['source_id'])
232 );
233 $results = CFF_Db::source_query($query_args);
234
235 if (! isset($results[0])) {
236 echo '{"error":{"message":"No valid ID found"}}';
237 wp_die();
238 }
239 $access_token = $results[0]['access_token'];
240
241 $url_or_post_id = $_POST['url_or_id'];
242
243 $id = CFF_Source::extract_id($url_or_post_id, 'album');
244
245
246 if (isset($id)) {
247 $data = CFF_Source::fetch_featured($id, sanitize_text_field($_POST['source_id']), $access_token);
248 echo $data;
249 } else {
250 echo '{"error":{"message":"No valid ID found"}}';
251 }
252
253 wp_die();
254 }
255
256 /**
257 * Using the URL or ID, info about a single playlist is returned
258 *
259 * @since 4.0
260 */
261 public static function get_playlist_post_preview()
262 {
263 check_ajax_referer('cff-admin', 'nonce');
264
265 $cap = current_user_can('manage_custom_facebook_feed_options') ? 'manage_custom_facebook_feed_options' : 'manage_options';
266 $cap = apply_filters('cff_settings_pages_capability', $cap);
267 if (! current_user_can($cap)) {
268 wp_send_json_error(); // This auto-dies.
269 }
270
271
272 $query_args = array(
273 'id' => sanitize_text_field($_POST['source_id'])
274 );
275 $results = CFF_Db::source_query($query_args);
276
277 if (! isset($results[0])) {
278 echo '{"error":{"message":"No valid ID found"}}';
279 wp_die();
280 }
281 $access_token = $results[0]['access_token'];
282
283 $url_or_post_id = $_POST['url_or_id'];
284
285 $id = CFF_Source::extract_id($url_or_post_id, 'playlist');
286
287 if (! empty($id)) {
288 $data = CFF_Source::fetch_playlist($id, $access_token);
289 echo $data;
290 } else {
291 echo '{"error":{"message":"Not a valid playlist for this account"}}';
292 }
293
294 wp_die();
295 }
296
297 /**
298 * Makes an API request to the featured post endpoint
299 *
300 * @param $post_id $url
301 * @param string $source_id
302 * @param string $access_token
303 *
304 * @return bool|string
305 *
306 * @since 4.0
307 */
308 public static function fetch_featured($post_id, $source_id, $access_token)
309 {
310 // need a connected business account for this to work
311 if (empty($access_token)) {
312 return false;
313 }
314
315 $full_id = $source_id . '_' . $post_id;
316 if (function_exists('cff_featured_post_id')) {
317 $featured_post_url = cff_featured_post_id($full_id, $access_token);
318 } else {
319 $featured_post_url = 'https://graph.facebook.com/v23.0/' . $full_id . '?fields=id,from{picture,id,name,link},message,message_tags,story,story_tags,picture,full_picture,status_type,created_time,backdated_time,attachments{title,description,media_type,unshimmed_url,target{id},multi_share_end_card,media{source,image},subattachments},shares,comments.summary(true){message,created_time},likes.summary(true).limit(0),call_to_action,privacy&access_token=' . $access_token;
320 }
321 if (function_exists('cff_featured_event_id')) {
322 $featured_event_url = cff_featured_event_id($full_id, $access_token);
323 } else {
324 $featured_event_url = 'https://graph.facebook.com/v23.0/' . $full_id . '?fields=id,name,attending_count,ticket_uri,cover,start_time,end_time,timezone,place,description,interested_count&access_token=' . $access_token;
325 }
326
327 $response = wp_safe_remote_get($featured_post_url);
328 $return = '{}';
329 if (! \CustomFacebookFeed\CFF_Utils::cff_is_wp_error($response)) {
330 if (! \CustomFacebookFeed\CFF_Utils::cff_is_fb_error($response['body'])) {
331 return $response['body'];
332 } else {
333 $return = $response['body'];
334 }
335 }
336
337 $response = wp_safe_remote_get($featured_event_url);
338 if (! \CustomFacebookFeed\CFF_Utils::cff_is_wp_error($response)) {
339 if (! \CustomFacebookFeed\CFF_Utils::cff_is_fb_error($response['body'])) {
340 return $response['body'];
341 }
342 } else {
343 return '{"error":{"message":"HTTP request error"}}';
344 }
345
346 return $return;
347 }
348
349 /**
350 * Makes an API request to the playlist endpoint
351 *
352 * @param $playlist_id $url
353 * @param string $access_token
354 *
355 * @return bool|string
356 *
357 * @since 4.0
358 */
359 public static function fetch_playlist($playlist_id, $access_token)
360 {
361 // need a connected business account for this to work
362 if (empty($access_token)) {
363 return false;
364 }
365
366 $url = 'https://graph.facebook.com/v3.2/' . $playlist_id . '/videos/?access_token=' . $access_token . '&fields=published,source,updated_time,created_time,title,description,embed_html,format{picture}&locale=en_US&limit=5';
367
368 $response = wp_safe_remote_get($url);
369 $return = '{}';
370 if (! \CustomFacebookFeed\CFF_Utils::cff_is_wp_error($response)) {
371 if (! \CustomFacebookFeed\CFF_Utils::cff_is_fb_error($response['body'])) {
372 return json_encode(
373 array_merge(
374 json_decode($response['body'], true),
375 [
376 'playlistID' => $playlist_id
377 ]
378 ),
379 true
380 );
381 } else {
382 $return = $response['body'];
383 }
384 }
385
386 return $return;
387 }
388
389 /**
390 * Connection URLs are based on the website connecting accounts so that is
391 * configured here and returned
392 *
393 * @return array
394 *
395 * @since 4.0
396 */
397 public static function get_connection_urls($is_settings = false)
398 {
399 $urls = array();
400 $admin_url_state = $is_settings ? admin_url('admin.php?page=cff-settings') : admin_url('admin.php?page=cff-feed-builder');
401 $sb_admin_email = get_option('admin_email');
402 $nonce = wp_create_nonce('cff_con');
403 $sw_flag = !empty($_GET['sw-feed']) ? true : false;
404 $groups_number = CFF_Db::check_group_source() > 0 ? true : false;
405
406
407 // If the admin_url isn't returned correctly then use a fallback
408 if (
409 $admin_url_state == '/wp-admin/admin.php?page=cff-feed-builder'
410 || $admin_url_state == '/wp-admin/admin.php?page=cff-feed-builder&tab=configuration'
411 ) {
412 $admin_url_state = "http://$_SERVER[HTTP_HOST]$_SERVER[REQUEST_URI]";
413 }
414
415 $urls['page'] = [
416 'connect' => CFF_CONNECT_URL,
417 'wordpress_user' => $sb_admin_email,
418 'v' => 'free',
419 'vn' => CFFVER,
420 'has_group' => $groups_number,
421 'cff_con' => $nonce,
422 'sw_feed' => $sw_flag
423 ];
424
425 $urls['group'] = [
426 'connect' => CFF_CONNECT_URL,
427 'wordpress_user' => $sb_admin_email,
428 'v' => 'free',
429 'vn' => CFFVER,
430 'has_group' => $groups_number,
431 'cff_con' => $nonce,
432 'sw_feed' => $sw_flag
433 ];
434 $urls['stateURL'] = $admin_url_state;
435 return $urls;
436 }
437
438 /**
439 * Used as a listener for the account connection process. If
440 * data is returned from the account connection processed it's used
441 * to generate the list of possible sources to chose from.
442 *
443 * @return array|bool
444 *
445 * @since 4.0
446 */
447 public static function maybe_source_connection_data()
448 {
449 $nonce = !empty($_GET['cff_con']) ? sanitize_key($_GET['cff_con']) : '';
450 if (!wp_verify_nonce($nonce, 'cff_con')) {
451 return false;
452 }
453
454 if (
455 isset($_GET['cff_access_token'])
456 && isset($_GET['cff_final_response'])
457 && $_GET['cff_final_response'] == 'true'
458 ) {
459 // clear access token errors since the user is reconnecting accounts
460 $reporter = \CustomFacebookFeed\CFF_Utils::cff_is_pro_version() ? cff_main_pro()->cff_error_reporter : cff_main()->cff_error_reporter;
461 $reporter->remove_error('accesstoken');
462
463 $access_token = sanitize_text_field($_GET['cff_access_token']);
464 if (isset($_GET['cff_group'])) {
465 $return = CFF_Source::retrieve_available_groups($access_token);
466 } else {
467 $return = CFF_Source::retrieve_available_pages($access_token);
468 }
469
470 if ($return) {
471 return $return;
472 } else {
473 return array( 'error' => __('Unable to connect to Facebook to retrieve account information.', 'custom-facebook-feed') );
474 }
475 }
476
477 return false;
478 }
479
480 /**
481 * Uses the Facebook API to retrieve a list of pages for the
482 * access token
483 *
484 * @param string $access_token
485 *
486 * @return array|bool
487 *
488 * @since 4.0
489 */
490 public static function retrieve_available_pages($access_token)
491 {
492
493 // Get User Info
494 $user_url = 'https://graph.facebook.com/me?fields=name,id,picture&access_token=' . $access_token;
495 $user_id_data = \CustomFacebookFeed\CFF_Utils::cff_fetchUrl($user_url);
496 $user_id_data_arr = json_decode($user_id_data);
497
498 $url = 'https://graph.facebook.com/me/accounts?fields=access_token,name,id&limit=500&access_token=' . $access_token;
499 $pages_data = \CustomFacebookFeed\CFF_Utils::cff_fetchUrl($url);
500 $pages_data_arr = json_decode($pages_data, true);
501
502 if (isset($pages_data_arr['data'])) {
503 $return = array(
504 'user' => $user_id_data_arr,
505 'pages' => $pages_data_arr['data'],
506 );
507
508 return $return;
509 } elseif (isset($pages_data_arr['error'])) {
510 return $pages_data_arr;
511 } else {
512 return [
513 'error' => [
514 'code' => 'HTTP Request',
515 'message' => __('Your server could not complete a remote request to Facebook\'s API. Your host may be blocking access or there may be a problem with your server.', 'custom-facebook-feed')
516 ]
517 ];
518 }
519 }
520
521 /**
522 * Uses the Facebook API to retrieve a list of groups for the
523 * access token split into "admin" and "member" groupings
524 *
525 * @param string $access_token
526 *
527 * @return array|bool
528 *
529 * @since 4.0
530 */
531 public static function retrieve_available_groups($access_token)
532 {
533 // Extend the user token by making a call to /me/accounts. User must be an admin of a page for this to work as won't work if the response is empty.
534 $url = 'https://graph.facebook.com/me/accounts?limit=500&access_token=' . $access_token;
535
536 $accounts_data = \CustomFacebookFeed\CFF_Utils::cff_fetchUrl($url);
537 if (empty($accounts_data)) {
538 return [
539 'error' => [
540 'code' => 'HTTP Request',
541 'message' => __('Your server could not complete a remote request to Facebook\'s API. Your host may be blocking access or there may be a problem with your server.', 'custom-facebook-feed')
542 ]
543 ];
544 }
545 $accounts_data_arr = json_decode($accounts_data);
546 $cff_token_expiration = 'never';
547 if (empty($accounts_data_arr->data)) {
548 $cff_token_expiration = '60 days';
549 }
550
551 if (! empty($accounts_data_arr->error)) {
552 return $accounts_data_arr;
553 }
554
555 // Get User Info
556 $user_url = 'https://graph.facebook.com/me?fields=name,id,picture&access_token=' . $access_token;
557 $user_id_data = \CustomFacebookFeed\CFF_Utils::cff_fetchUrl($user_url);
558
559 if (! empty($user_id_data)) {
560 $user_id_data_arr = json_decode($user_id_data);
561
562 $user_id = $user_id_data_arr->id;
563
564 $admin_ids = [];
565 // Get groups they're admin of
566 $groups_admin_url = 'https://graph.facebook.com/' . $user_id . '/groups?admin_only=true&fields=name,id,picture&access_token=' . $access_token;
567 $groups_admin_data = \CustomFacebookFeed\CFF_Utils::cff_fetchUrl($groups_admin_url);
568 $groups_admin_data_arr = json_decode($groups_admin_data, true);
569 $admin_groups = array();
570 if (isset($groups_admin_data_arr['data'])) {
571 foreach ($groups_admin_data_arr['data'] as $single_group) {
572 $single_group['expiration'] = $cff_token_expiration;
573 $single_group['access_token'] = $access_token;
574 $admin_groups[] = $single_group;
575 $admin_ids[] = $single_group['id'];
576 }
577 }
578
579 // Get member groups
580 $groups_url = 'https://graph.facebook.com/' . $user_id . '/groups?admin_only=false&fields=name,id,picture&access_token=' . $access_token;
581 $groups_data = \CustomFacebookFeed\CFF_Utils::cff_fetchUrl($groups_url);
582 $groups_data_arr = json_decode($groups_data, true);
583 $member_groups = array();
584 if (isset($groups_data_arr['data'])) {
585 foreach ($groups_data_arr['data'] as $single_group) {
586 if (! in_array($single_group['id'], $admin_ids, true)) {
587 $single_group['expiration'] = $cff_token_expiration;
588 $single_group['access_token'] = $access_token;
589 $member_groups[] = $single_group;
590 }
591 }
592 }
593 $return = array(
594 'user' => $user_id_data_arr,
595 'admin' => $admin_groups,
596 'member' => $member_groups
597 );
598
599 return $return;
600 } elseif (isset($accounts_data_arr['error'])) {
601 return $accounts_data;
602 } else {
603 return [
604 'error' => [
605 'code' => 'HTTP Request',
606 'message' => __('Your server could not complete a remote request to Facebook\'s API. Your host may be blocking access or there may be a problem with your server.', 'custom-facebook-feed')
607 ]
608 ];
609 }
610 }
611
612 /**
613 * Used to update or insert connected accounts (sources)
614 *
615 * @param array $source_data
616 *
617 * @return bool
618 *
619 * @since 4.0
620 */
621 public static function update_or_insert($source_data)
622 {
623 if (! isset($source_data['id'])) {
624 return false;
625 }
626
627 if (isset($source_data['info'])) {
628 // data from an API request related to the source is saved as a JSON string
629 if (is_object($source_data['info']) || is_array($source_data['info'])) {
630 $source_data['info'] = \CustomFacebookFeed\CFF_Utils::cff_json_encode($source_data['info']);
631 }
632 }
633
634 if (CFF_Source::exists_in_database($source_data)) {
635 $source_data['last_updated'] = date('Y-m-d H:i:s');
636 CFF_Source::update($source_data);
637 } else {
638 if (! isset($source_data['access_token'])) {
639 return false;
640 }
641 CFF_Source::insert($source_data);
642 }
643
644 CFF_Source::after_update_or_insert($source_data);
645 }
646
647 /**
648 * Whether or not the source exists in the database
649 *
650 * @param array $args
651 *
652 * @return bool
653 *
654 * @since 4.0
655 */
656 public static function exists_in_database($args)
657 {
658 $results = CFF_Db::source_query($args);
659
660 return isset($results[0]);
661 }
662
663 /**
664 * Add a new source as a row in the cff_sources table
665 *
666 * @param array $source_data
667 *
668 * @return false|int
669 *
670 * @since 4.0
671 */
672 public static function insert($source_data)
673 {
674 $source_data['username'] = $source_data['name'];
675 $data = $source_data;
676
677 return CFF_Db::source_insert($data);
678 }
679
680 /**
681 * Update info in rows that match the source data
682 *
683 * @param array $source_data
684 *
685 * @return false|int
686 *
687 * @since 4.0
688 */
689 public static function update($source_data, $where_privilige = true)
690 {
691 $where = array( 'id' => $source_data['id'] );
692 unset($source_data['id']);
693
694 if ($where_privilige && isset($source_data['privilege'])) {
695 $where['privilege'] = $source_data['privilege'];
696 }
697
698 // usernames are more common in the other plugins so
699 // that is the name of the column that is used as the
700 // page or group "name" data
701 if (isset($source_data['name'])) {
702 $source_data['username'] = $source_data['name'];
703 }
704 $data = $source_data;
705
706 return CFF_Db::source_update($data, $where);
707 }
708
709 /**
710 * Do something after a source is updated or inserted
711 *
712 * @param array $source_data
713 * @since 4.0.6/4.0.9
714 */
715 public static function after_update_or_insert($source_data)
716 {
717
718 // check to see if all groups updated
719 $cff_statuses_option = get_option('cff_statuses', array());
720
721 if (empty($cff_statuses_option['groups_need_update'])) {
722 return;
723 }
724 $groups = \CustomFacebookFeed\Builder\CFF_Db::source_query(array( 'type' => 'group' ));
725
726 $cff_statuses_option['groups_need_update'] = false;
727 if (empty($groups)) {
728 update_option('cff_statuses', $cff_statuses_option, false);
729 } else {
730 $encryption = new \CustomFacebookFeed\SB_Facebook_Data_Encryption();
731 $groups_need_update = false;
732 foreach ($groups as $source) {
733 $info = ! empty($source['info']) ? json_decode($encryption->decrypt($source['info'])) : array();
734 if (\CustomFacebookFeed\Builder\CFF_Source::needs_update($source, $info)) {
735 $groups_need_update = true;
736 }
737 }
738 $cff_statuses_option['groups_need_update'] = $groups_need_update;
739 update_option('cff_statuses', $cff_statuses_option, false);
740 }
741 }
742
743 /**
744 * @param array $source
745 * @param array $source_info
746 *
747 * @return bool
748 * @since 4.0.6/4.0.9
749 */
750 public static function needs_update($source, $source_info)
751 {
752 if ('group' === $source['account_type']) {
753 $connected_version = is_object($source_info) && isset($source_info->connected_version) ? $source_info->connected_version : 0;
754 if (version_compare($connected_version, '4.0.9', '<')) {
755 return true;
756 }
757 }
758
759 return false;
760 }
761
762 /**
763 * Attempts to find the album or playlist ID from
764 * a Facebook URL
765 *
766 * @param string $url_or_post_id
767 * @param string $type
768 *
769 * @return bool|mixed|string
770 *
771 * @since 4.0
772 */
773 public static function extract_id($url_or_post_id, $type)
774 {
775 $id = false;
776
777 if ($type === 'album') {
778 if (strpos($url_or_post_id, '/') === false) {
779 $id = sanitize_text_field($url_or_post_id);
780 } elseif (strpos($url_or_post_id, '&set=a.') !== false) {
781 $things = explode('&set=a.', $url_or_post_id);
782
783 $id = $things[1];
784 } else {
785 $regex = '/(?:https?:\/\/)?(?:www\.)?facebook\.com\/(?:(?:\w\.)*#!\/)?(?:pages\/)?(?:[\w\-\.]*\/)*([\w\-\.]*)/';
786 if (preg_match($regex, $url_or_post_id, $matches)) {
787 if (isset($matches[0])) {
788 $id = end($matches);
789 }
790 }
791 }
792 } elseif ($type === 'playlist') {
793 if (strpos($url_or_post_id, '/') === false) {
794 $id = sanitize_text_field($url_or_post_id);
795 } else { // https://www.facebook.com/watch/539051002877739/1979731855647067/?more=less // https://www.facebook.com/videos/vl.1234567890/
796 $regex = "~(?:vl\.\d+/)?(\d+)~i";
797 if (stripos($url_or_post_id, 'videos') !== false && preg_match($regex, $url_or_post_id, $matches)) {
798 if (isset($matches[0])) {
799 $id_pieces = end($matches);
800
801 if (strpos($id_pieces, '.') !== false) {
802 $id = explode('.', $id_pieces)[1];
803 } else {
804 $id = $matches[0];
805 }
806 }
807 } else {
808 if (strpos($url_or_post_id, '?') !== false) {
809 $url_or_post_id = explode('?', $url_or_post_id)[0];
810 }
811
812 $parts = explode('/', $url_or_post_id);
813
814
815 $id = $parts[ count($parts) - 1 ];
816
817 if (empty($id)) {
818 $id = $parts[ count($parts) - 2 ];
819 }
820
821 $id = (int) filter_var($id, FILTER_SANITIZE_NUMBER_INT);
822 }
823 }
824 }
825
826 return $id;
827 }
828
829 /**
830 * Creates a queue of connected accounts that need to be added to
831 * the sources table
832 *
833 * @since 4.0.3
834 */
835 public static function set_legacy_source_queue()
836 {
837 $cff_statuses_option = get_option('cff_statuses', array());
838 $connected_accounts = (array)json_decode(stripcslashes(get_option('cff_connected_accounts')), true);
839
840 $cff_statuses_option['legacy_source_queue'] = array_chunk(array_keys($connected_accounts), CFF_Source::BATCH_SIZE);
841
842 update_option('cff_statuses', $cff_statuses_option);
843 }
844
845 /**
846 * Whether or not there are still sources in the queue and
847 * this isn't disabled
848 *
849 * @return bool
850 *
851 * @since 4.0.3
852 */
853 public static function should_do_source_updates()
854 {
855 $cff_statuses_option = get_option('cff_statuses', array());
856
857 $should_do_source_updates = isset($cff_statuses_option['legacy_source_queue']) ? ! empty($cff_statuses_option['legacy_source_queue']) : false;
858
859 return apply_filters('should_do_source_updates', $should_do_source_updates);
860 }
861
862 /**
863 * Processes one set of connected accounts
864 *
865 * @since 4.0.3
866 */
867 public static function batch_process_legacy_source_queue()
868 {
869 if (! CFF_Source::should_do_source_updates()) {
870 return;
871 }
872
873 $cff_statuses_option = get_option('cff_statuses', array());
874 $batch = array_shift($cff_statuses_option['legacy_source_queue']);
875 update_option('cff_statuses', $cff_statuses_option); // updated early just in case there is a fatal error
876
877 if (empty($batch)) {
878 return;
879 }
880 $connected_accounts = (array)json_decode(stripcslashes(get_option('cff_connected_accounts')), true);
881 foreach ($batch as $account_key) {
882 $connected_account = isset($connected_accounts[ $account_key ]) ? $connected_accounts[ $account_key ] : false;
883
884 if ($connected_account) {
885 CFF_Source::update_single_source($connected_account);
886 }
887 }
888 }
889
890 /**
891 * Transfer data from a connected account to the sources table
892 * after it's been validated with an API call
893 *
894 * @param array $connected_account
895 *
896 * @since 4.0.3
897 */
898 public static function update_single_source($connected_account)
899 {
900 $cff_page_slugs = get_option('cff_page_slugs', array());
901 $access_token = $connected_account['accesstoken'];
902 $id = str_replace(' ', '', $connected_account['id']);
903 $header_details = \CustomFacebookFeed\CFF_Utils::fetch_header_data($id, $connected_account['pagetype'] === 'group', $access_token, 0, false, '');
904
905 $source_data = array(
906 'access_token' => $connected_account['accesstoken'],
907 'id' => $connected_account['id'],
908 'type' => $connected_account['pagetype'],
909 'name' => $connected_account['name'],
910 'privilege' => '', // see if events token?
911 );
912
913 if (! is_numeric($id)) {
914 if (! isset($cff_page_slugs[ $id ])) {
915 $cff_page_slugs[ $id ] = $header_details->id;
916 update_option('cff_page_slugs', $cff_page_slugs);
917 }
918 $source_data['id'] = $header_details->id;
919 }
920
921 if (isset($header_details->shortcode_options)) {
922 unset($header_details->shortcode_options);
923 }
924
925 if (isset($header_details->name)) {
926 $source_data['name'] = $header_details->name;
927 }
928 $source_data['info'] = $header_details;
929 $source_data['error'] = '';
930
931 if (isset($header_details->error) || isset($header_details->cached_error)) {
932 $source_data['error'] = isset($header_details->error) ? \CustomFacebookFeed\CFF_Utils::cff_json_encode($header_details->error) : \CustomFacebookFeed\CFF_Utils::cff_json_encode($header_details->cached_error);
933 }
934
935 CFF_Source::update_or_insert($source_data);
936
937 $source_data['record_id'] = 0;
938 $source_data['account_id'] = $connected_account['id'];
939 $source_data['account_type'] = $connected_account['pagetype'];
940 $source_data['username'] = $source_data['name'];
941
942 return $source_data;
943 }
944
945 /**
946 * Creates a source from the access token and
947 * source ID saved in 3.x settings
948 *
949 * @since 4.0.3
950 */
951 public static function update_source_from_legacy_settings()
952 {
953 $db_access_token_option = get_option('cff_access_token');
954 $db_page_access_token = get_option('cff_page_access_token');
955 $db_page_id_option = get_option('cff_page_id');
956 $db_page_type = get_option('cff_page_type');
957 $cff_page_slugs = get_option('cff_page_slugs', array());
958
959 if (
960 (! empty($db_access_token_option) || ! empty($db_page_access_token))
961 && ! empty($db_page_id_option)
962 ) {
963 $db_access_tokens = explode(',', str_replace(' ', '', $db_access_token_option));
964 $db_page_ids = explode(',', str_replace(' ', '', $db_page_id_option));
965
966 $i = 0;
967 foreach ($db_access_tokens as $db_access_token) {
968 if (strpos($db_access_token, ':') !== false) {
969 $id_at_arr = explode(':', $db_access_token);
970 $db_page_id = $id_at_arr[0];
971 $db_access_token = $id_at_arr[1];
972 } else {
973 $db_page_id = $db_page_ids[ $i ];
974 }
975 $source_data = array(
976 'access_token' => ! empty($db_page_access_token) ? $db_page_access_token : $db_access_token,
977 'id' => $db_page_id,
978 'type' => $db_page_type === 'group' ? 'group' : 'page',
979 'name' => $db_page_id,
980 'privilege' => '', // see if events token?
981 );
982
983 $header_details = \CustomFacebookFeed\CFF_Utils::fetch_header_data($source_data['id'], $source_data['type'] === 'group', $source_data['access_token'], 0, false, '');
984
985 if (isset($header_details->shortcode_options)) {
986 unset($header_details->shortcode_options);
987 }
988
989 if (isset($header_details->name)) {
990 $source_data['name'] = $header_details->name;
991 }
992
993 if (! is_numeric($source_data['id']) && isset($header_details->id)) {
994 if (! isset($cff_page_slugs[ $source_data['id'] ])) {
995 $cff_page_slugs[ $source_data['id'] ] = $header_details->id;
996 update_option('cff_page_slugs', $cff_page_slugs);
997 }
998 $source_data['id'] = $header_details->id;
999 }
1000
1001 $source_data['info'] = $header_details;
1002
1003 // don't update or insert the access token if there is an API error
1004 if (! isset($header_details->error) && ! isset($header_details->cached_error)) {
1005 \CustomFacebookFeed\Builder\CFF_Source::update_or_insert($source_data);
1006 } else {
1007 if (! empty($db_page_access_token) && ! empty($db_access_token)) {
1008 $source_data = array(
1009 'access_token' => $db_access_token,
1010 'id' => $db_page_id,
1011 'type' => $db_page_type === 'group' ? 'group' : 'page',
1012 'name' => $db_page_id,
1013 'privilege' => '', // see if events token?
1014 );
1015
1016 $header_details = \CustomFacebookFeed\CFF_Utils::fetch_header_data($source_data['id'], $source_data['type'] === 'group', $source_data['access_token'], 0, false, '');
1017
1018 if (isset($header_details->shortcode_options)) {
1019 unset($header_details->shortcode_options);
1020 }
1021
1022 if (isset($header_details->name)) {
1023 $source_data['name'] = $header_details->name;
1024 }
1025 $source_data['info'] = $header_details;
1026
1027 if (! isset($header_details->error) && ! isset($header_details->cached_error)) {
1028 \CustomFacebookFeed\Builder\CFF_Source::update_or_insert($source_data);
1029 }
1030 }
1031 }
1032 $i++;
1033 }
1034 }
1035 }
1036
1037 /**
1038 * If the plugin is still updating legacy sources this function
1039 * can be used to udpate a single source if needed before
1040 * the update is done.
1041 *
1042 * @param string $slug_or_id
1043 *
1044 * @return array|bool
1045 */
1046 public static function maybe_one_off_connected_account_update($slug_or_id)
1047 {
1048 if (! CFF_Source::should_do_source_updates()) {
1049 return false;
1050 }
1051
1052 $connected_accounts = (array)json_decode(stripcslashes(get_option('cff_connected_accounts')), true);
1053 $connected_account = isset($connected_accounts[ $slug_or_id ]) ? $connected_accounts[ $slug_or_id ] : false;
1054
1055 if ($connected_account) {
1056 return CFF_Source::update_single_source($connected_account);
1057 }
1058
1059 return false;
1060 }
1061
1062 /**
1063 * Get the Facebook ID using an alphanumeric page "slug" if
1064 * it has been created in the DB
1065 *
1066 * @param string $slug
1067 *
1068 * @return bool|string
1069 *
1070 * @since 4.0.3
1071 */
1072 public static function get_id_from_slug($slug)
1073 {
1074 $cff_page_slugs = get_option('cff_page_slugs', array());
1075 if (isset($cff_page_slugs[ $slug ])) {
1076 return $cff_page_slugs[ $slug ];
1077 }
1078 return false;
1079 }
1080
1081 /**
1082 * Clears the "error" column in the cff_sources table for a specific
1083 * account
1084 *
1085 * @param string $account_id
1086 *
1087 * @return bool
1088 *
1089 * @since 4.0.3
1090 */
1091 public static function clear_error($account_id)
1092 {
1093 $source_data = array(
1094 'id' => $account_id,
1095 'error' => ''
1096 );
1097 return \CustomFacebookFeed\Builder\CFF_Source::update_or_insert($source_data);
1098 }
1099
1100 /**
1101 * Adds an error to the error table by account ID
1102 *
1103 * @param string $account_id
1104 * @param string|object|array $error
1105 *
1106 * @return bool
1107 *
1108 * @since 4.0.3
1109 */
1110 public static function add_error($account_id, $error)
1111 {
1112 $source_data = array(
1113 'id' => $account_id,
1114 'error' => is_string($error) ? $error : \CustomFacebookFeed\CFF_Utils::cff_json_encode($error)
1115 );
1116 return \CustomFacebookFeed\Builder\CFF_Source::update_or_insert($source_data);
1117 }
1118
1119
1120 /**
1121 * Add error to the option table "cff_error_reporter"
1122 *
1123 * @param string $account_id
1124 * @param string|object|array $error
1125 *
1126 * @return bool
1127 *
1128 * @since 4.0.3
1129 */
1130 public static function add_report_error_option($account)
1131 {
1132 $errors = get_option('cff_error_reporter', []);
1133 $add_error = (isset($errors['connection']) && isset($errors['connection']['error_id']) && $errors['connection']['error_id'] == 190) ? false : true;
1134 if ($add_error) {
1135 $url = 'https://graph.facebook.com/v23.0/' . $account['account_id'] . '/posts?fields=id,updated_time,from{picture,id,name,link},message,message_tags,story,story_tags,status_type,created_time,backdated_time,call_to_action,attachments{title,description,media_type,unshimmed_url,target{id},media{source}}&access_token=' . $account['access_token'] . '&limit=7&locale=en_US';
1136 CFF_Utils::cff_fetchUrl($url, false);
1137 }
1138 }
1139
1140
1141 /**
1142 * Get single source info By Id
1143 *
1144 * @since 4.2.0
1145 */
1146 public static function get_single_source_info($source_id)
1147 {
1148 $query_args = array(
1149 'id' => sanitize_text_field(wp_unslash($source_id))
1150 );
1151 $results = CFF_Db::source_query_byid($query_args);
1152 if (!isset($results[0])) {
1153 return false;
1154 }
1155 return $results[0];
1156 }
1157
1158 /**
1159 * Should show group Notice
1160 *
1161 * @since 4.2.0
1162 */
1163 public static function should_show_group_deprecation()
1164 {
1165 $cff_statuses = get_option('cff_statuses', array());
1166 return (
1167 !isset($cff_statuses['cff_group_deprecation_dismiss']) ||
1168 $cff_statuses['cff_group_deprecation_dismiss'] !== true
1169 ) && CFF_Db::check_group_source() > 0;
1170 }
1171 }
1172