diff options
Diffstat (limited to 'plugins/Facebook')
-rw-r--r-- | plugins/Facebook/README | 10 | ||||
-rw-r--r-- | plugins/Facebook/facebook/facebook.php | 74 | ||||
-rwxr-xr-x | plugins/Facebook/facebook/facebookapi_php5_restlib.php | 56 | ||||
-rw-r--r-- | plugins/Facebook/facebooksettings.php | 21 | ||||
-rw-r--r-- | plugins/Facebook/facebookutil.php | 258 |
5 files changed, 251 insertions, 168 deletions
diff --git a/plugins/Facebook/README b/plugins/Facebook/README index 14c1d3241..532f1d82e 100644 --- a/plugins/Facebook/README +++ b/plugins/Facebook/README @@ -38,11 +38,11 @@ editor or write them down. In Facebook's application editor, specify the following URLs for your app: -- Canvas Callback URL : http://example.net/mublog/facebook/app/ -- Post-Remove Callback URL: http://example.net/mublog/facebook/app/remove -- Post-Add Redirect URL : http://apps.facebook.com/yourapp/ -- Canvas Page URL : http://apps.facebook.com/yourapp/ -- Connect URL : http://example.net/mublog/ +- Canvas Callback URL : http://example.net/mublog/facebook/app/ +- Post-Remove Callback URL : http://example.net/mublog/facebook/app/remove +- Post-Authorize Redirect URL : http://apps.facebook.com/yourapp/ +- Canvas Page URL : http://apps.facebook.com/yourapp/ +- Connect URL : http://example.net/mublog/ *** ATTENTION *** These URLs have changed slightly since StatusNet version 0.8.1, diff --git a/plugins/Facebook/facebook/facebook.php b/plugins/Facebook/facebook/facebook.php index 440706cbc..76696c1d5 100644 --- a/plugins/Facebook/facebook/facebook.php +++ b/plugins/Facebook/facebook/facebook.php @@ -45,7 +45,9 @@ class Facebook { public $user; public $profile_user; public $canvas_user; + public $ext_perms = array(); protected $base_domain; + /* * Create a Facebook client like this: * @@ -104,17 +106,17 @@ class Facebook { * * For nitty-gritty details of when each of these is used, check out * http://wiki.developers.facebook.com/index.php/Verifying_The_Signature - * - * @param bool resolve_auth_token convert an auth token into a session */ - public function validate_fb_params($resolve_auth_token=true) { + public function validate_fb_params() { $this->fb_params = $this->get_valid_fb_params($_POST, 48 * 3600, 'fb_sig'); // note that with preload FQL, it's possible to receive POST params in // addition to GET, so use a different prefix to differentiate them if (!$this->fb_params) { $fb_params = $this->get_valid_fb_params($_GET, 48 * 3600, 'fb_sig'); - $fb_post_params = $this->get_valid_fb_params($_POST, 48 * 3600, 'fb_post_sig'); + $fb_post_params = $this->get_valid_fb_params($_POST, + 48 * 3600, // 48 hours + 'fb_post_sig'); $this->fb_params = array_merge($fb_params, $fb_post_params); } @@ -128,6 +130,9 @@ class Facebook { $this->fb_params['canvas_user'] : null; $this->base_domain = isset($this->fb_params['base_domain']) ? $this->fb_params['base_domain'] : null; + $this->ext_perms = isset($this->fb_params['ext_perms']) ? + explode(',', $this->fb_params['ext_perms']) + : array(); if (isset($this->fb_params['session_key'])) { $session_key = $this->fb_params['session_key']; @@ -141,13 +146,11 @@ class Facebook { $this->set_user($user, $session_key, $expires); - } - // if no Facebook parameters were found in the GET or POST variables, - // then fall back to cookies, which may have cached user information - // Cookies are also used to receive session data via the Javascript API - else if ($cookies = - $this->get_valid_fb_params($_COOKIE, null, $this->api_key)) { - + } else if ($cookies = + $this->get_valid_fb_params($_COOKIE, null, $this->api_key)) { + // if no Facebook parameters were found in the GET or POST variables, + // then fall back to cookies, which may have cached user information + // Cookies are also used to receive session data via the Javascript API $base_domain_cookie = 'base_domain_' . $this->api_key; if (isset($_COOKIE[$base_domain_cookie])) { $this->base_domain = $_COOKIE[$base_domain_cookie]; @@ -160,25 +163,6 @@ class Facebook { $cookies['session_key'], $expires); } - // finally, if we received no parameters, but the 'auth_token' GET var - // is present, then we are in the middle of auth handshake, - // so go ahead and create the session - else if ($resolve_auth_token && isset($_GET['auth_token']) && - $session = $this->do_get_session($_GET['auth_token'])) { - if ($this->generate_session_secret && - !empty($session['secret'])) { - $session_secret = $session['secret']; - } - - if (isset($session['base_domain'])) { - $this->base_domain = $session['base_domain']; - } - - $this->set_user($session['uid'], - $session['session_key'], - $session['expires'], - isset($session_secret) ? $session_secret : null); - } return !empty($this->fb_params); } @@ -309,11 +293,28 @@ class Facebook { // require_add and require_install have been removed. // see http://developer.facebook.com/news.php?blog=1&story=116 for more details - public function require_login() { - if ($user = $this->get_loggedin_user()) { + public function require_login($required_permissions = '') { + $user = $this->get_loggedin_user(); + $has_permissions = true; + + if ($required_permissions) { + $this->require_frame(); + $permissions = array_map('trim', explode(',', $required_permissions)); + foreach ($permissions as $permission) { + if (!in_array($permission, $this->ext_perms)) { + $has_permissions = false; + break; + } + } + } + + if ($user && $has_permissions) { return $user; } - $this->redirect($this->get_login_url(self::current_url(), $this->in_frame())); + + $this->redirect( + $this->get_login_url(self::current_url(), $this->in_frame(), + $required_permissions)); } public function require_frame() { @@ -342,10 +343,11 @@ class Facebook { return $page . '?' . http_build_query($params); } - public function get_login_url($next, $canvas) { + public function get_login_url($next, $canvas, $req_perms = '') { $page = self::get_facebook_url().'/login.php'; - $params = array('api_key' => $this->api_key, - 'v' => '1.0'); + $params = array('api_key' => $this->api_key, + 'v' => '1.0', + 'req_perms' => $req_perms); if ($next) { $params['next'] = $next; diff --git a/plugins/Facebook/facebook/facebookapi_php5_restlib.php b/plugins/Facebook/facebook/facebookapi_php5_restlib.php index fa1088cd0..e249a326b 100755 --- a/plugins/Facebook/facebook/facebookapi_php5_restlib.php +++ b/plugins/Facebook/facebook/facebookapi_php5_restlib.php @@ -569,7 +569,7 @@ function toggleDisplay(id, type) { return $this->call_method('facebook.events.invite', array('eid' => $eid, 'uids' => $uids, - 'personal_message', $personal_message)); + 'personal_message' => $personal_message)); } /** @@ -1350,53 +1350,6 @@ function toggleDisplay(id, type) { ); } - /** - * Dashboard API - */ - - /** - * Set the news for the specified user. - * - * @param int $uid The user for whom you are setting news for - * @param string $news Text of news to display - * - * @return bool Success - */ - public function dashboard_setNews($uid, $news) { - return $this->call_method('facebook.dashboard.setNews', - array('uid' => $uid, - 'news' => $news) - ); - } - - /** - * Get the current news of the specified user. - * - * @param int $uid The user to get the news of - * - * @return string The text of the current news for the user - */ - public function dashboard_getNews($uid) { - return json_decode( - $this->call_method('facebook.dashboard.getNews', - array('uid' => $uid) - ), true); - } - - /** - * Set the news for the specified user. - * - * @param int $uid The user you are clearing the news of - * - * @return bool Success - */ - public function dashboard_clearNews($uid) { - return $this->call_method('facebook.dashboard.clearNews', - array('uid' => $uid) - ); - } - - /** * Creates a note with the specified title and content. @@ -2005,7 +1958,7 @@ function toggleDisplay(id, type) { * @return array A list of strings describing any compile errors for the * submitted FBML */ - function profile_setFBML($markup, + public function profile_setFBML($markup, $uid=null, $profile='', $profile_action='', @@ -3267,9 +3220,8 @@ function toggleDisplay(id, type) { } else { $get['v'] = '1.0'; } - if (isset($this->use_ssl_resources) && - $this->use_ssl_resources) { - $post['return_ssl_resources'] = true; + if (isset($this->use_ssl_resources)) { + $post['return_ssl_resources'] = (bool) $this->use_ssl_resources; } return array($get, $post); } diff --git a/plugins/Facebook/facebooksettings.php b/plugins/Facebook/facebooksettings.php index 766d0e199..f94a346b5 100644 --- a/plugins/Facebook/facebooksettings.php +++ b/plugins/Facebook/facebooksettings.php @@ -54,22 +54,11 @@ class FacebooksettingsAction extends FacebookAction $noticesync = $this->boolean('noticesync'); $replysync = $this->boolean('replysync'); - $prefix = $this->trimmed('prefix'); $original = clone($this->flink); $this->flink->set_flags($noticesync, false, $replysync, false); $result = $this->flink->update($original); - if ($prefix == '' || $prefix == '0') { - // Facebook bug: saving empty strings to prefs now fails - // http://bugs.developers.facebook.com/show_bug.cgi?id=7110 - $trimmed = $prefix . ' '; - } else { - $trimmed = substr($prefix, 0, 128); - } - $this->facebook->api_client->data_setUserPreference(FACEBOOK_NOTICE_PREFIX, - $trimmed); - if ($result === false) { $this->showForm(_m('There was a problem saving your sync preferences!')); } else { @@ -110,16 +99,6 @@ class FacebooksettingsAction extends FacebookAction $this->elementStart('li'); - $prefix = trim($this->facebook->api_client->data_getUserPreference(FACEBOOK_NOTICE_PREFIX)); - - $this->input('prefix', _m('Prefix'), - ($prefix) ? $prefix : null, - _m('A string to prefix notices with.')); - - $this->elementEnd('li'); - - $this->elementStart('li'); - $this->submit('save', _m('Save')); $this->elementEnd('li'); diff --git a/plugins/Facebook/facebookutil.php b/plugins/Facebook/facebookutil.php index ab2d42726..1290fed55 100644 --- a/plugins/Facebook/facebookutil.php +++ b/plugins/Facebook/facebookutil.php @@ -81,101 +81,251 @@ function isFacebookBound($notice, $flink) { function facebookBroadcastNotice($notice) { $facebook = getFacebook(); - $flink = Foreign_link::getByUserID($notice->profile_id, FACEBOOK_SERVICE); + $flink = Foreign_link::getByUserID( + $notice->profile_id, + FACEBOOK_SERVICE + ); if (isFacebookBound($notice, $flink)) { // Okay, we're good to go, update the FB status - $status = null; $fbuid = $flink->foreign_id; $user = $flink->getUser(); - $attachments = $notice->attachments(); try { - // Get the status 'verb' (prefix) the user has set + // Check permissions - // XXX: Does this call count against our per user FB request limit? - // If so we should consider storing verb elsewhere or not storing + common_debug( + 'FacebookPlugin - checking for publish_stream permission for user ' + . "$user->nickname ($user->id), Facebook UID: $fbuid" + ); - $prefix = trim($facebook->api_client->data_getUserPreference(FACEBOOK_NOTICE_PREFIX, - $fbuid)); + // NOTE: $facebook->api_client->users_hasAppPermission('publish_stream', $fbuid) + // has been returning bogus results, so we're using FQL to check for + // publish_stream permission now - $status = "$prefix $notice->content"; + $fql = "SELECT publish_stream FROM permissions WHERE uid = $fbuid"; + $result = $facebook->api_client->fql_query($fql); - common_debug("FacebookPlugin - checking for publish_stream permission for user $user->id"); + $canPublish = 0; - $can_publish = $facebook->api_client->users_hasAppPermission('publish_stream', - $fbuid); + if (!empty($result)) { + $canPublish = $result[0]['publish_stream']; + } + + if ($canPublish == 1) { + common_debug( + "FacebookPlugin - $user->nickname ($user->id), Facebook UID: $fbuid " + . 'has publish_stream permission.' + ); + } else { + common_debug( + "FacebookPlugin - $user->nickname ($user->id), Facebook UID: $fbuid " + . 'does NOT have publish_stream permission. Facebook ' + . 'returned: ' . var_export($result, true) + ); + } - common_debug("FacebookPlugin - checking for status_update permission for user $user->id"); + common_debug( + 'FacebookPlugin - checking for status_update permission for user ' + . "$user->nickname ($user->id), Facebook UID: $fbuid. " + ); + + $canUpdate = $facebook->api_client->users_hasAppPermission( + 'status_update', + $fbuid + ); + + if ($canUpdate == 1) { + common_debug( + "FacebookPlugin - $user->nickname ($user->id), Facebook UID: $fbuid " + . 'has status_update permission.' + ); + } else { + common_debug( + "FacebookPlugin - $user->nickname ($user->id), Facebook UID: $fbuid " + .'does NOT have status_update permission. Facebook ' + . 'returned: ' . var_export($canPublish, true) + ); + } - $can_update = $facebook->api_client->users_hasAppPermission('status_update', - $fbuid); - if (!empty($attachments) && $can_publish == 1) { - $fbattachment = format_attachments($attachments); - $facebook->api_client->stream_publish($status, $fbattachment, - null, null, $fbuid); - common_log(LOG_INFO, - "FacebookPlugin - Posted notice $notice->id w/attachment " . - "to Facebook user's stream (fbuid = $fbuid)."); - } elseif ($can_update == 1 || $can_publish == 1) { - $facebook->api_client->users_setStatus($status, $fbuid, false, true); - common_log(LOG_INFO, - "FacebookPlugin - Posted notice $notice->id to Facebook " . - "as a status update (fbuid = $fbuid)."); + // Post to Facebook + + if ($notice->hasAttachments() && $canPublish == 1) { + publishStream($notice, $user, $fbuid); + } elseif ($canUpdate == 1 || $canPublish == 1) { + statusUpdate($notice, $user, $fbuid); } else { $msg = "FacebookPlugin - Not sending notice $notice->id to Facebook " . - "because user $user->nickname hasn't given the " . + "because user $user->nickname has not given the " . 'Facebook app \'status_update\' or \'publish_stream\' permission.'; common_log(LOG_WARNING, $msg); } // Finally, attempt to update the user's profile box - if ($can_publish == 1 || $can_update == 1) { - updateProfileBox($facebook, $flink, $notice); + if ($canPublish == 1 || $canUpdate == 1) { + updateProfileBox($facebook, $flink, $notice, $user); } } catch (FacebookRestClientException $e) { + return handleFacebookError($e, $notice, $flink); + } + } - $code = $e->getCode(); - - $msg = "FacebookPlugin - Facebook returned error code $code: " . - $e->getMessage() . ' - ' . - "Unable to update Facebook status (notice $notice->id) " . - "for $user->nickname (user id: $user->id)!"; + return true; +} - common_log(LOG_WARNING, $msg); +function handleFacebookError($e, $notice, $flink) +{ + $fbuid = $flink->foreign_id; + $user = $flink->getUser(); + $code = $e->getCode(); + $errmsg = $e->getMessage(); + + // XXX: Check for any others? + switch($code) { + case 100: // Invalid parameter + $msg = "FacebookPlugin - Facebook claims notice %d was posted with an invalid parameter (error code 100):" + . "\"%s\" (Notice details: nickname=%s, user ID=%d, Facebook ID=%d, notice content=\"%s\"). " + . "Removing notice from the Facebook queue for safety."; + common_log( + LOG_ERR, sprintf( + $msg, + $notice->id, + $errmsg, + $user->nickname, + $user->id, + $fbuid, + $notice->content + ) + ); + return true; + break; + case 200: // Permissions error + case 250: // Updating status requires the extended permission status_update + remove_facebook_app($flink); + return true; // dequeue + break; + case 341: // Feed action request limit reached + $msg = "FacebookPlugin - User %s (User ID=%d, Facebook ID=%d) has exceeded " + . "his/her limit for posting notices to Facebook today. Dequeuing " + . "notice %d."; + common_log( + LOG_INFO, sprintf( + $msg, + $user->nickname, + $user->id, + $fbuid, + $notice->id + ) + ); + // @fixme: We want to rety at a later time when the throttling has expired + // instead of just giving up. + return true; + break; + default: + $msg = "FacebookPlugin - Facebook returned an error we don't know how to deal with while trying to " + . "post notice %d. Error code: %d, error message: \"%s\". (Notice details: " + . "nickname=%s, user ID=%d, Facebook ID=%d, notice content=\"%s\"). Removing notice " + . "from the Facebook queue for safety."; + common_log( + LOG_ERR, sprintf( + $msg, + $notice->id, + $code, + $errmsg, + $user->nickname, + $user->id, + $fbuid, + $notice->content + ) + ); + return true; // dequeue + break; + } +} - if ($code == 100 || $code == 200 || $code == 250) { +function statusUpdate($notice, $user, $fbuid) +{ + common_debug( + "FacebookPlugin - Attempting to post notice $notice->id " + . "as a status update for $user->nickname ($user->id), " + . "Facebook UID: $fbuid" + ); - // 100 The account is 'inactive' (probably - this is not well documented) - // 200 The application does not have permission to operate on the passed in uid parameter. - // 250 Updating status requires the extended permission status_update or publish_stream. - // see: http://wiki.developers.facebook.com/index.php/Users.setStatus#Example_Return_XML + $facebook = getFacebook(); + $result = $facebook->api_client->users_setStatus( + $notice->content, + $fbuid, + false, + true + ); + + common_debug('Facebook returned: ' . var_export($result, true)); + + common_log( + LOG_INFO, + "FacebookPlugin - Posted notice $notice->id as a status " + . "update for $user->nickname ($user->id), " + . "Facebook UID: $fbuid" + ); +} - remove_facebook_app($flink); +function publishStream($notice, $user, $fbuid) +{ + common_debug( + "FacebookPlugin - Attempting to post notice $notice->id " + . "as stream item with attachment for $user->nickname ($user->id), " + . "Facebook UID: $fbuid" + ); - } else { + $fbattachment = format_attachments($notice->attachments()); - // Try sending again later. + $facebook = getFacebook(); + $facebook->api_client->stream_publish( + $notice->content, + $fbattachment, + null, + null, + $fbuid + ); + + common_log( + LOG_INFO, + "FacebookPlugin - Posted notice $notice->id as a stream " + . "item with attachment for $user->nickname ($user->id), " + . "Facebook UID: $fbuid" + ); +} - return false; - } +function updateProfileBox($facebook, $flink, $notice, $user) { - } - } + $facebook = getFacebook(); + $fbaction = new FacebookAction( + $output = 'php://output', + $indent = null, + $facebook, + $flink + ); - return true; + $fbuid = $flink->foreign_id; -} + common_debug( + 'FacebookPlugin - Attempting to update profile box with ' + . "content from notice $notice->id for $user->nickname ($user->id), " + . "Facebook UID: $fbuid" + ); -function updateProfileBox($facebook, $flink, $notice) { - $fbaction = new FacebookAction($output = 'php://output', - $indent = null, $facebook, $flink); $fbaction->updateProfileBox($notice); + + common_debug( + 'FacebookPlugin - finished updating profile box for ' + . "$user->nickname ($user->id) Facebook UID: $fbuid" + ); + } function format_attachments($attachments) |