Recent Facebook Posts - Version 1.0.5

Version Description

  • Added: More user-friendly error message when cURL is not enabled on your server.
Download this release

Release Info

Developer DvanKooten
Plugin Icon wp plugin Recent Facebook Posts
Version 1.0.5
Comparing to
See all releases

Version 1.0.5

classes/class-rfb-admin.php ADDED
@@ -0,0 +1,107 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class RFB_Admin {
4
+
5
+ public function __construct($RFB) {
6
+ global $pagenow;
7
+
8
+ add_action('admin_init', array(&$this, 'register_settings'));
9
+ add_action('admin_menu', array(&$this, 'build_menu'));
10
+
11
+ add_filter("plugin_action_links_recent-facebook-posts/recent-facebook-posts.php", array(&$this, 'add_settings_link'));
12
+
13
+ $this->RFB = $RFB;
14
+
15
+ if(isset($_GET['rfb_renew_access_token'])) {
16
+ $this->renew_access_token();
17
+ }
18
+
19
+ if(isset($_GET['page']) && $_GET['page'] == 'rfb-settings') {
20
+ $this->handle_requests();
21
+ }
22
+ }
23
+
24
+ private function renew_access_token() {
25
+ $fb = $this->RFB->get_fb_instance();
26
+
27
+ // Get Facebook User ID
28
+ $user = $fb->getUser();
29
+
30
+ if($user) {
31
+ $access_token = $fb->getAccessToken();
32
+ // store access token for later usage
33
+ update_option('rfb_access_token', $access_token);
34
+ }
35
+ }
36
+
37
+ public function register_settings() {
38
+ register_setting('rfb_settings_group', 'rfb_settings');
39
+ }
40
+
41
+ public function build_menu() {
42
+ $page = add_options_page('Recent Facebook Posts - Settings','Recent Facebook Posts','manage_options','rfb-settings',array($this, 'settings_page'));
43
+ add_action( 'admin_print_styles-' . $page, array(&$this, 'load_css') );
44
+ }
45
+
46
+ public function load_css() {
47
+ wp_enqueue_style( 'rfb_admin_css', plugins_url('recent-facebook-posts/css/admin.css') );
48
+ }
49
+
50
+ public function settings_page () {
51
+
52
+ $opts = $this->RFB->get_options();
53
+ $curl = extension_loaded('curl');
54
+ $fb = $this->RFB->get_fb_instance();
55
+ //update_option('rfb_access_token', '');
56
+ $access_token = get_option('rfb_access_token');
57
+ $connected = false;
58
+
59
+ // try to fetch a test post
60
+ if($curl && $access_token) {
61
+ $fb->setAccessToken($access_token);
62
+
63
+ $connected = $fb->getUser();
64
+
65
+ if($connected) {
66
+ try {
67
+ $try = $fb->api('/' . $opts['fb_id'] . '/posts?limit=1');
68
+ } catch(Exception $e) {
69
+ $connected = false;
70
+ $error = $e;
71
+ }
72
+ }
73
+ }
74
+
75
+ // check if cache directory is writable
76
+ $cache_dir = dirname(__FILE__) . '/../cache/';
77
+ $cache_file = dirname(__FILE__) . '/../cache/posts.cache';
78
+
79
+ if(!is_writable($cache_dir)) {
80
+ $cache_error = 'The cache directory (<i>'. ABSPATH . 'wp-content/plugins/recent-facebook-posts/cache/</i>) is not writable.';
81
+ } elseif(file_exists($cache_file) && !is_writable($cache_file)) {
82
+ $cache_error = 'The cache directory (<i>'. ABSPATH . 'wp-content/plugins/recent-facebook-posts/cache/posts.cache</i>) exists but is not writable.';
83
+ }
84
+
85
+
86
+ require dirname(__FILE__) . '/../views/settings_page.html.php';
87
+ }
88
+
89
+ public function handle_requests() {
90
+ if(isset($_POST['renew_cache'])) {
91
+ add_action('init', array($this->RFB, 'renew_cache_file'));
92
+ }
93
+ }
94
+
95
+
96
+ /**
97
+ * Adds the settings link on the plugin's overview page
98
+ * @param array $links Array containing all the settings links for the various plugins.
99
+ * @return array The new array containing all the settings links
100
+ */
101
+ function add_settings_link($links) {
102
+ $settings_link = '<a href="options-general.php?page=rfb-settings">Settings</a>';
103
+ array_unshift($links, $settings_link);
104
+
105
+ return $links;
106
+ }
107
+ }
classes/class-rfb-widget.php ADDED
@@ -0,0 +1,163 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class RFB_Widget extends WP_Widget {
4
+
5
+ private $defaults = array(
6
+ 'title' => 'Recent Facebook posts',
7
+ 'number_of_posts' => 5,
8
+ 'excerpt_length' => 140,
9
+ 'show_comment_count' => false,
10
+ 'show_like_count' => false,
11
+ 'show_link' => false
12
+ );
13
+
14
+ public function __construct() {
15
+ parent::__construct(
16
+ 'rfb_widget', // Base ID
17
+ 'Recent Facebook Posts', // Name
18
+ array( 'description' => 'Lists a number of your most recent Facebook posts.' )
19
+ );
20
+ }
21
+
22
+ public function form( $instance ) {
23
+
24
+ $instance = array_merge($this->defaults, $instance);
25
+ extract($instance);
26
+
27
+ global $RFB;
28
+ $rfb_options = $RFB->get_options();
29
+
30
+ if(empty($rfb_options['app_id'])) { ?>
31
+ <p style="color:red;">You'll need to <a href="<?php echo get_admin_url(null, 'options-general.php?page=rfb-settings'); ?>">configure Recent Facebook Posts</a> in order for it to work.</p>
32
+ <?php } ?>
33
+ <p>
34
+ <label for="<?php echo $this->get_field_id( 'title' ); ?>"><?php _e( 'Title:' ); ?></label>
35
+ <input class="widefat" id="<?php echo $this->get_field_id( 'title' ); ?>" name="<?php echo $this->get_field_name( 'title' ); ?>" type="text" value="<?php echo esc_attr( $title ); ?>" />
36
+ </p>
37
+
38
+ <p>
39
+ <label for="<?php echo $this->get_field_id( 'number_of_posts' ); ?>"><?php _e( 'Number of posts:' ); ?></label>
40
+ <input class="widefat" id="<?php echo $this->get_field_id( 'number_of_posts' ); ?>" name="<?php echo $this->get_field_name( 'number_of_posts' ); ?>" type="text" value="<?php echo esc_attr( $number_of_posts ); ?>" />
41
+ </p>
42
+
43
+ <p>
44
+ <label for="<?php echo $this->get_field_id( 'excerpt_length' ); ?>"><?php _e( 'Excerpt length:' ); ?></label>
45
+ <input class="widefat" id="<?php echo $this->get_field_id( 'excerpt_length' ); ?>" name="<?php echo $this->get_field_name( 'excerpt_length' ); ?>" type="text" value="<?php echo esc_attr( $excerpt_length ); ?>" />
46
+ </p>
47
+
48
+ <p>
49
+ <input type="checkbox" id="<?php echo $this->get_field_id( 'show_like_count' ); ?>" name="<?php echo $this->get_field_name( 'show_like_count' ); ?>" value="1" <?php if($show_like_count) { ?>checked="1"<?php } ?> />
50
+ <label for="<?php echo $this->get_field_id( 'show_like_count' ); ?>"><?php _e( 'Show Like count?' ); ?></label>
51
+ </p>
52
+
53
+ <p>
54
+ <input type="checkbox" id="<?php echo $this->get_field_id( 'show_comment_count' ); ?>" name="<?php echo $this->get_field_name( 'show_comment_count' ); ?>" value="1" <?php if($show_comment_count) { ?>checked="1"<?php } ?> />
55
+ <label for="<?php echo $this->get_field_id( 'show_comment_count' ); ?>"><?php _e( 'Show Comment count?' ); ?></label>
56
+ </p>
57
+
58
+ <p>
59
+ <input type="checkbox" id="<?php echo $this->get_field_id( 'show_link' ); ?>" name="<?php echo $this->get_field_name( 'show_link' ); ?>" value="1" <?php if($show_link) { ?>checked="1"<?php } ?> />
60
+ <label for="<?php echo $this->get_field_id( 'show_link' ); ?>"><?php _e( 'Show a link to Facebook page?' ); ?></label>
61
+ </p>
62
+
63
+ <p style="border: 2px solid green; font-weight:bold; background: #CFC; padding:5px; ">I spent countless hours developing (and offering support) for this plugin for FREE. If you like it, consider <a href="http://dannyvankooten.com/donate/">donating $10, $20 or $50</a> as a token of your appreciation.</p>
64
+
65
+ <?php
66
+ }
67
+
68
+ public function update( $new_instance, $old_instance ) {
69
+ $instance = array();
70
+ $instance['title'] = strip_tags( $new_instance['title'] );
71
+ $instance['number_of_posts'] = (int) strip_tags( $new_instance['number_of_posts'] );
72
+ $instance['excerpt_length'] = (int) strip_tags($new_instance['excerpt_length']);
73
+ $instance['show_like_count'] = isset($new_instance['show_like_count']);
74
+ $instance['show_comment_count'] = isset($new_instance['show_comment_count']);
75
+ $instance['show_link'] = isset($new_instance['show_link']);
76
+ return $instance;
77
+ }
78
+
79
+ public function widget( $args, $instance ) {
80
+ global $RFB;
81
+
82
+ $opts = $RFB->get_options();
83
+ $posts = $RFB->get_posts();
84
+ $posts = array_slice($posts, 0, $instance['number_of_posts']);
85
+
86
+ extract( $args );
87
+ $instance = array_merge($this->defaults, $instance);
88
+ extract($instance);
89
+
90
+ $title = apply_filters( 'widget_title', $instance['title'] );
91
+
92
+ echo $before_widget;
93
+ if ( ! empty( $title ) )
94
+ echo $before_title . $title . $after_title; ?>
95
+ <ul class="rfb_posts">
96
+ <?php foreach($posts as $post) { ?>
97
+ <?php
98
+ $content = $post['content'];
99
+ $shortened = false;
100
+
101
+ if(strlen($content) > $instance['excerpt_length']) {
102
+ $limit = strpos($post['content'], ' ',$instance['excerpt_length']);
103
+ if($limit) {
104
+ $content = substr($post['content'], 0, $limit);
105
+ $shortened = true;
106
+ }
107
+ }
108
+
109
+ ?>
110
+ <li>
111
+ <p class="rfb_text"><?php echo nl2br(make_clickable($content)); if($shortened) echo '..'; ?></p>
112
+ <?php if(isset($post['image']) && $post['image']) { ?><p class="rfb_image"><a target="_blank" href="<?php echo $post['link']; ?>" rel="nofollow"><img src="<?php echo $post['image']; ?>" alt="" /></a></p><?php } ?>
113
+ <p><a target="_blank" class="rfb_link" href="<?php echo $post['link']; ?>" rel="nofollow">
114
+ <?php if($show_like_count || $show_comment_count) { ?><span class="like_count_and_comment_count"><?php } ?>
115
+ <?php if($show_like_count) { ?><span class="like_count"><?php echo $post['like_count']; ?> <span>likes</span></span> <?php } ?>
116
+ <?php if($show_comment_count) { ?><span class="comment_count"><?php echo $post['comment_count']; ?> <span>comments</span></span> <?php } ?>
117
+ <?php if($show_like_count || $show_comment_count) { ?></span><?php } ?>
118
+ <span class="timestamp" title="<?php echo date('l, F j, Y', $post['timestamp']) . ' at ' . date('G:i', $post['timestamp']); ?>" ><?php if($show_like_count || $show_comment_count) { ?> · <?php } ?><span><?php echo $this->time_ago($post['timestamp']); ?></span></span>
119
+ </a></p>
120
+ </li>
121
+ <?php }
122
+
123
+ if(empty($posts)) { ?>
124
+ <li>
125
+ <p>No recent Facebook status updates to show.</p>
126
+ <?php if(current_user_can('manage_options')) { ?><p><strong>Admins only notice:</strong> Did you <a href="<?php echo get_admin_url(null,'options-general.php?page=rfb-settings'); ?>">configure the plugin</a> properly?<?php } ?></p>
127
+ </li>
128
+
129
+ <?php } ?>
130
+ </ul>
131
+
132
+ <?php if($show_link) { ?>
133
+ <p><a href="http://www.facebook.com/<?php echo $opts['fb_id']; ?>/" rel="external nofollow"><?php echo strip_tags($opts['link_text']); ?></a>.</p>
134
+ <?php } ?>
135
+
136
+ <?php
137
+
138
+ echo $after_widget;
139
+ }
140
+
141
+ private function time_ago($timestamp) {
142
+ $diff = time() - (int) $timestamp;
143
+
144
+ if ($diff == 0)
145
+ return 'just now';
146
+
147
+ $intervals = array
148
+ (
149
+ 1 => array('year', 31556926),
150
+ $diff < 31556926 => array('month', 2628000),
151
+ $diff < 2629744 => array('week', 604800),
152
+ $diff < 604800 => array('day', 86400),
153
+ $diff < 86400 => array('hour', 3600),
154
+ $diff < 3600 => array('minute', 60),
155
+ $diff < 60 => array('second', 1)
156
+ );
157
+
158
+ $value = floor($diff/$intervals[1][1]);
159
+ return $value.' '.$intervals[1][0].($value > 1 ? 's' : '').' ago';
160
+
161
+ }
162
+
163
+ }
classes/class-rfb.php ADDED
@@ -0,0 +1,166 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class RFB {
4
+
5
+ private static $instance;
6
+ private static $fb_instance;
7
+ private $defaults = array(
8
+ 'app_id' => '',
9
+ 'app_secret' => '',
10
+ 'fb_id' => 'DannyvanKootenCOM',
11
+ 'cache_time' => 1800,
12
+ 'load_css' => 0,
13
+ 'link_text' => 'Find us on Facebook'
14
+ );
15
+ private $options;
16
+
17
+ public static function get_instance() {
18
+ if(!self::$instance) self::$instance = new RFB();
19
+ return self::$instance;
20
+ }
21
+
22
+ public function __construct() {
23
+ add_action('wp_login', array($this, 'renew_access_token'));
24
+ add_action('init', array(&$this, 'on_init'));
25
+
26
+ // only on frontend
27
+ if(!is_admin()) {
28
+ $opts = $this->get_options();
29
+ if($opts['load_css']) {
30
+ add_action( 'wp_enqueue_scripts', array(&$this, 'load_css'));
31
+ }
32
+ }
33
+ }
34
+
35
+ public function on_init() {
36
+ if(!session_id() && !headers_sent()) {
37
+ session_start();
38
+ }
39
+ }
40
+
41
+ public function load_css() {
42
+ wp_register_style('rfb_css', plugins_url('recent-facebook-posts/css/rfb.css') );
43
+ wp_enqueue_style('rfb_css' );
44
+ }
45
+
46
+ public function get_options() {
47
+ if(!$this->options) {
48
+ $this->options = array_merge($this->defaults, (array) get_option('rfb_settings'));
49
+ }
50
+
51
+ return $this->options;
52
+ }
53
+
54
+ public function renew_access_token() {
55
+ // only run if this is an administrator.
56
+ $user = wp_get_current_user();
57
+
58
+ if(!user_can($user, 'manage_options')) return false;
59
+
60
+ $fb = $this->get_fb_instance();
61
+
62
+ // Get Facebook User ID
63
+ $fb_user = $fb->getUser();
64
+
65
+ if(!$fb_user) {
66
+
67
+ if(!headers_sent()) {
68
+ header("Location: ".$fb->getLoginUrl(array('redirect_uri' => get_admin_url() . '?rfb_renew_access_token')));
69
+ exit;
70
+ } else {
71
+ echo '<script type="text/javascript">window.location = \''. $fb->getLoginUrl(array('redirect_uri' => get_admin_url() . '?rfb_renew_access_token')) .'\';</script>';
72
+ exit;
73
+ }
74
+ }
75
+
76
+ return;
77
+ }
78
+
79
+ public function get_fb_instance() {
80
+ if(!self::$fb_instance) {
81
+ require dirname(__FILE__) . '/facebook-php-sdk/facebook.php';
82
+ $opts = $this->get_options();
83
+ self::$fb_instance = new Facebook(array(
84
+ 'appId' => $opts['app_id'],
85
+ 'secret' => $opts['app_secret'],
86
+ ));
87
+ }
88
+ return self::$fb_instance;
89
+ }
90
+
91
+ public function get_posts() {
92
+ $cache_file = dirname(__FILE__) . '/../cache/posts.cache';
93
+ $opts = $this->get_options();
94
+
95
+ // check if cache file exists
96
+ // check if cache file is exists for longer then the given expiration time
97
+ if(!file_exists($cache_file) || (filemtime($cache_file) < (time() - $opts['cache_time']))) {
98
+ $this->renew_cache_file();
99
+
100
+ if(!file_exists($cache_file)) return array();
101
+ }
102
+
103
+ $posts = json_decode(file_get_contents($cache_file), true);
104
+ return $posts;
105
+ }
106
+
107
+ public function renew_cache_file() {
108
+ $opts = $this->get_options();
109
+ if(empty($opts['app_id']) || empty($opts['fb_id'])) return false;
110
+
111
+ $access_token = get_option('rfb_access_token');
112
+ if(!$access_token) {
113
+
114
+ $access_token = $this->renew_access_token();
115
+ if(!$access_token) return false;
116
+ }
117
+
118
+ $fb = $this->get_fb_instance();
119
+ $fb->setAccessToken($access_token);
120
+
121
+ if(!$fb->getUser()) return false;
122
+
123
+ $apiResult = $fb->api($opts['fb_id'].'/posts', "GET", array(
124
+ 'limit' => 250
125
+ )
126
+ );
127
+
128
+ if(!$apiResult or !is_array($apiResult) or !isset($apiResult['data']) or !is_array($apiResult['data'])) { return false; }
129
+
130
+ $data = array();
131
+ foreach($apiResult['data'] as $p) {
132
+ if(!isset($p['message']) || empty($p['message'])) continue;
133
+
134
+ //split user and post ID (userID_postID)
135
+ $idArray = explode("_", $p['id']);
136
+
137
+ $post = array();
138
+ $post['author'] = $p['from'];
139
+ $post['content'] = $p['message'];
140
+
141
+
142
+ if($p['type'] == 'photo') {
143
+ $post['image'] = $p['picture'];
144
+ } else {
145
+ $post['image'] = null;
146
+ }
147
+
148
+ $post['timestamp'] = strtotime($p['created_time']);
149
+ $post['like_count'] = (isset($p['likes'])) ? $p['likes']['count'] : 0;
150
+ $post['comment_count'] = (isset($p['comments'])) ? $p['comments']['count'] : 0;
151
+ $post['link'] = "http://www.facebook.com/".$opts['fb_id']."/posts/".$idArray[1];
152
+ $data[] = $post;
153
+
154
+ }
155
+
156
+ $data = json_encode($data);
157
+ $cache_file = dirname(__FILE__) . '/../cache/posts.cache';
158
+
159
+ if(!is_writable(dirname(__FILE__) . '/../cache/')) return false;
160
+
161
+ file_put_contents($cache_file, $data);
162
+
163
+ return true;
164
+ }
165
+
166
+ }
classes/facebook-php-sdk/base_facebook.php ADDED
@@ -0,0 +1,1425 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Copyright 2011 Facebook, Inc.
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may
6
+ * not use this file except in compliance with the License. You may obtain
7
+ * a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14
+ * License for the specific language governing permissions and limitations
15
+ * under the License.
16
+ */
17
+
18
+ if (!function_exists('curl_init')) {
19
+ throw new Exception('Facebook needs the CURL PHP extension.');
20
+ }
21
+ if (!function_exists('json_decode')) {
22
+ throw new Exception('Facebook needs the JSON PHP extension.');
23
+ }
24
+
25
+ /**
26
+ * Thrown when an API call returns an exception.
27
+ *
28
+ * @author Naitik Shah <naitik@facebook.com>
29
+ */
30
+ class FacebookApiException extends Exception
31
+ {
32
+ /**
33
+ * The result from the API server that represents the exception information.
34
+ */
35
+ protected $result;
36
+
37
+ /**
38
+ * Make a new API Exception with the given result.
39
+ *
40
+ * @param array $result The result from the API server
41
+ */
42
+ public function __construct($result) {
43
+ $this->result = $result;
44
+
45
+ $code = isset($result['error_code']) ? $result['error_code'] : 0;
46
+
47
+ if (isset($result['error_description'])) {
48
+ // OAuth 2.0 Draft 10 style
49
+ $msg = $result['error_description'];
50
+ } else if (isset($result['error']) && is_array($result['error'])) {
51
+ // OAuth 2.0 Draft 00 style
52
+ $msg = $result['error']['message'];
53
+ } else if (isset($result['error_msg'])) {
54
+ // Rest server style
55
+ $msg = $result['error_msg'];
56
+ } else {
57
+ $msg = 'Unknown Error. Check getResult()';
58
+ }
59
+
60
+ parent::__construct($msg, $code);
61
+ }
62
+
63
+ /**
64
+ * Return the associated result object returned by the API server.
65
+ *
66
+ * @return array The result from the API server
67
+ */
68
+ public function getResult() {
69
+ return $this->result;
70
+ }
71
+
72
+ /**
73
+ * Returns the associated type for the error. This will default to
74
+ * 'Exception' when a type is not available.
75
+ *
76
+ * @return string
77
+ */
78
+ public function getType() {
79
+ if (isset($this->result['error'])) {
80
+ $error = $this->result['error'];
81
+ if (is_string($error)) {
82
+ // OAuth 2.0 Draft 10 style
83
+ return $error;
84
+ } else if (is_array($error)) {
85
+ // OAuth 2.0 Draft 00 style
86
+ if (isset($error['type'])) {
87
+ return $error['type'];
88
+ }
89
+ }
90
+ }
91
+
92
+ return 'Exception';
93
+ }
94
+
95
+ /**
96
+ * To make debugging easier.
97
+ *
98
+ * @return string The string representation of the error
99
+ */
100
+ public function __toString() {
101
+ $str = $this->getType() . ': ';
102
+ if ($this->code != 0) {
103
+ $str .= $this->code . ': ';
104
+ }
105
+ return $str . $this->message;
106
+ }
107
+ }
108
+
109
+ /**
110
+ * Provides access to the Facebook Platform. This class provides
111
+ * a majority of the functionality needed, but the class is abstract
112
+ * because it is designed to be sub-classed. The subclass must
113
+ * implement the four abstract methods listed at the bottom of
114
+ * the file.
115
+ *
116
+ * @author Naitik Shah <naitik@facebook.com>
117
+ */
118
+ abstract class BaseFacebook
119
+ {
120
+ /**
121
+ * Version.
122
+ */
123
+ const VERSION = '3.2.0';
124
+
125
+ /**
126
+ * Signed Request Algorithm.
127
+ */
128
+ const SIGNED_REQUEST_ALGORITHM = 'HMAC-SHA256';
129
+
130
+ /**
131
+ * Default options for curl.
132
+ */
133
+ public static $CURL_OPTS = array(
134
+ CURLOPT_CONNECTTIMEOUT => 10,
135
+ CURLOPT_RETURNTRANSFER => true,
136
+ CURLOPT_TIMEOUT => 60,
137
+ CURLOPT_USERAGENT => 'facebook-php-3.2',
138
+ );
139
+
140
+ /**
141
+ * List of query parameters that get automatically dropped when rebuilding
142
+ * the current URL.
143
+ */
144
+ protected static $DROP_QUERY_PARAMS = array(
145
+ 'code',
146
+ 'state',
147
+ 'signed_request',
148
+ );
149
+
150
+ /**
151
+ * Maps aliases to Facebook domains.
152
+ */
153
+ public static $DOMAIN_MAP = array(
154
+ 'api' => 'https://api.facebook.com/',
155
+ 'api_video' => 'https://api-video.facebook.com/',
156
+ 'api_read' => 'https://api-read.facebook.com/',
157
+ 'graph' => 'https://graph.facebook.com/',
158
+ 'graph_video' => 'https://graph-video.facebook.com/',
159
+ 'www' => 'https://www.facebook.com/',
160
+ );
161
+
162
+ /**
163
+ * The Application ID.
164
+ *
165
+ * @var string
166
+ */
167
+ protected $appId;
168
+
169
+ /**
170
+ * The Application App Secret.
171
+ *
172
+ * @var string
173
+ */
174
+ protected $appSecret;
175
+
176
+ /**
177
+ * The ID of the Facebook user, or 0 if the user is logged out.
178
+ *
179
+ * @var integer
180
+ */
181
+ protected $user;
182
+
183
+ /**
184
+ * The data from the signed_request token.
185
+ */
186
+ protected $signedRequest;
187
+
188
+ /**
189
+ * A CSRF state variable to assist in the defense against CSRF attacks.
190
+ */
191
+ protected $state;
192
+
193
+ /**
194
+ * The OAuth access token received in exchange for a valid authorization
195
+ * code. null means the access token has yet to be determined.
196
+ *
197
+ * @var string
198
+ */
199
+ protected $accessToken = null;
200
+
201
+ /**
202
+ * Indicates if the CURL based @ syntax for file uploads is enabled.
203
+ *
204
+ * @var boolean
205
+ */
206
+ protected $fileUploadSupport = false;
207
+
208
+ /**
209
+ * Indicates if we trust HTTP_X_FORWARDED_* headers.
210
+ *
211
+ * @var boolean
212
+ */
213
+ protected $trustForwarded = false;
214
+
215
+ /**
216
+ * Initialize a Facebook Application.
217
+ *
218
+ * The configuration:
219
+ * - appId: the application ID
220
+ * - secret: the application secret
221
+ * - fileUpload: (optional) boolean indicating if file uploads are enabled
222
+ *
223
+ * @param array $config The application configuration
224
+ */
225
+ public function __construct($config) {
226
+ $this->setAppId($config['appId']);
227
+ $this->setAppSecret($config['secret']);
228
+ if (isset($config['fileUpload'])) {
229
+ $this->setFileUploadSupport($config['fileUpload']);
230
+ }
231
+ if (isset($config['trustForwarded']) && $config['trustForwarded']) {
232
+ $this->trustForwarded = true;
233
+ }
234
+ $state = $this->getPersistentData('state');
235
+ if (!empty($state)) {
236
+ $this->state = $state;
237
+ }
238
+ }
239
+
240
+ /**
241
+ * Set the Application ID.
242
+ *
243
+ * @param string $appId The Application ID
244
+ * @return BaseFacebook
245
+ */
246
+ public function setAppId($appId) {
247
+ $this->appId = $appId;
248
+ return $this;
249
+ }
250
+
251
+ /**
252
+ * Get the Application ID.
253
+ *
254
+ * @return string the Application ID
255
+ */
256
+ public function getAppId() {
257
+ return $this->appId;
258
+ }
259
+
260
+ /**
261
+ * Set the App Secret.
262
+ *
263
+ * @param string $apiSecret The App Secret
264
+ * @return BaseFacebook
265
+ * @deprecated
266
+ */
267
+ public function setApiSecret($apiSecret) {
268
+ $this->setAppSecret($apiSecret);
269
+ return $this;
270
+ }
271
+
272
+ /**
273
+ * Set the App Secret.
274
+ *
275
+ * @param string $appSecret The App Secret
276
+ * @return BaseFacebook
277
+ */
278
+ public function setAppSecret($appSecret) {
279
+ $this->appSecret = $appSecret;
280
+ return $this;
281
+ }
282
+
283
+ /**
284
+ * Get the App Secret.
285
+ *
286
+ * @return string the App Secret
287
+ * @deprecated
288
+ */
289
+ public function getApiSecret() {
290
+ return $this->getAppSecret();
291
+ }
292
+
293
+ /**
294
+ * Get the App Secret.
295
+ *
296
+ * @return string the App Secret
297
+ */
298
+ public function getAppSecret() {
299
+ return $this->appSecret;
300
+ }
301
+
302
+ /**
303
+ * Set the file upload support status.
304
+ *
305
+ * @param boolean $fileUploadSupport The file upload support status.
306
+ * @return BaseFacebook
307
+ */
308
+ public function setFileUploadSupport($fileUploadSupport) {
309
+ $this->fileUploadSupport = $fileUploadSupport;
310
+ return $this;
311
+ }
312
+
313
+ /**
314
+ * Get the file upload support status.
315
+ *
316
+ * @return boolean true if and only if the server supports file upload.
317
+ */
318
+ public function getFileUploadSupport() {
319
+ return $this->fileUploadSupport;
320
+ }
321
+
322
+ /**
323
+ * DEPRECATED! Please use getFileUploadSupport instead.
324
+ *
325
+ * Get the file upload support status.
326
+ *
327
+ * @return boolean true if and only if the server supports file upload.
328
+ */
329
+ public function useFileUploadSupport() {
330
+ return $this->getFileUploadSupport();
331
+ }
332
+
333
+ /**
334
+ * Sets the access token for api calls. Use this if you get
335
+ * your access token by other means and just want the SDK
336
+ * to use it.
337
+ *
338
+ * @param string $access_token an access token.
339
+ * @return BaseFacebook
340
+ */
341
+ public function setAccessToken($access_token) {
342
+ $this->accessToken = $access_token;
343
+ return $this;
344
+ }
345
+
346
+ /**
347
+ * Extend an access token, while removing the short-lived token that might
348
+ * have been generated via client-side flow. Thanks to http://bit.ly/b0Pt0H
349
+ * for the workaround.
350
+ */
351
+ public function setExtendedAccessToken() {
352
+ try {
353
+ // need to circumvent json_decode by calling _oauthRequest
354
+ // directly, since response isn't JSON format.
355
+ $access_token_response = $this->_oauthRequest(
356
+ $this->getUrl('graph', '/oauth/access_token'),
357
+ $params = array(
358
+ 'client_id' => $this->getAppId(),
359
+ 'client_secret' => $this->getAppSecret(),
360
+ 'grant_type' => 'fb_exchange_token',
361
+ 'fb_exchange_token' => $this->getAccessToken(),
362
+ )
363
+ );
364
+ }