]> git.mxchange.org Git - quix0rs-gnu-social.git/commitdiff
Merge branch 'master' of gitorious.org:statusnet/mainline
authorBrion Vibber <brion@pobox.com>
Tue, 25 May 2010 20:11:36 +0000 (13:11 -0700)
committerBrion Vibber <brion@pobox.com>
Tue, 25 May 2010 20:11:36 +0000 (13:11 -0700)
classes/Notice.php
plugins/Facebook/facebook/facebook.php
plugins/Facebook/facebook/facebookapi_php5_restlib.php
plugins/Facebook/facebookutil.php

index e173a2469095d7fd4410ecaee7333fe78322b904..d85c8cd33acf802c0f566016cfc632b0cd0bd809 100644 (file)
@@ -97,15 +97,20 @@ class Notice extends Memcached_DataObject
         // For auditing purposes, save a record that the notice
         // was deleted.
 
-        $deleted = new Deleted_notice();
+        // @fixme we have some cases where things get re-run and so the
+        // insert fails.
+        $deleted = Deleted_notice::staticGet('id', $this->id);
+        if (!$deleted) {
+            $deleted = new Deleted_notice();
 
-        $deleted->id         = $this->id;
-        $deleted->profile_id = $this->profile_id;
-        $deleted->uri        = $this->uri;
-        $deleted->created    = $this->created;
-        $deleted->deleted    = common_sql_now();
+            $deleted->id         = $this->id;
+            $deleted->profile_id = $this->profile_id;
+            $deleted->uri        = $this->uri;
+            $deleted->created    = $this->created;
+            $deleted->deleted    = common_sql_now();
 
-        $deleted->insert();
+            $deleted->insert();
+        }
 
         // Clear related records
 
index 440706cbc3fdb5543001003e88e2bc9721414192..76696c1d557abf8fe58b9ad40538900b394a9cfc 100644 (file)
@@ -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;
index fa1088cd00bffedbcd3d7af4593b5d4e998b7c1f..e249a326b219f067d6bf42756e9bdcd2a9ac4453 100755 (executable)
@@ -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);
   }
index 045891649c3f39f459ee4b447837b5f91571375f..c7b0f02c31df6d1d0f2049e50b01e68dd789658f 100644 (file)
@@ -81,114 +81,286 @@ 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'];
+            }
 
-            common_debug("FacebookPlugin - checking for status_update permission for user $user->id");
+            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)
+                );
+            }
 
-            $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).");
+            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)
+                );
+            }
+
+            // 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();
+    return true;
+}
 
-            $msg = "FacebookPlugin - Facebook returned error code $code: " .
-              $e->getMessage() . ' - ' .
-              "Unable to update Facebook status (notice $notice->id) " .
-              "for $user->nickname (user id: $user->id)!";
+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;
+    }
+}
 
-            common_log(LOG_WARNING, $msg);
+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"
+    );
 
-            if ($code == 100 || $code == 200 || $code == 250) {
+    $text = formatNotice($notice, $user, $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(
+         $text,
+         $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 if ($code == 341) {
-                // 341 Feed action request limit reached - Unable to update Facebook status
-                // Reposting immediately probably won't work, so drop the message for now. :(
+    $text = formatNotice($notice, $user, $fbuid);
+    $fbattachment = format_attachments($notice->attachments());
 
-                common_log(LOG_ERR, "Facebook rate limit hit: dropping notice $notice->id");
-                return true;
-            } else {
+    $facebook = getFacebook();
+    $facebook->api_client->stream_publish(
+        $text,
+        $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"
+    );
+}
 
-                // Try sending again later.
-                //
-                // @fixme at the moment, returning false here could lead to an infinite loop
-                // if the error condition isn't actually transitory.
-                //
-                // Temporarily throwing an exception to kill the process so it'll hit our
-                // retry limits.
-                throw new Exception("Facebook error $code on notice $notice->id");
+function formatNotice($notice, $user, $fbuid)
+{
+    // Get the status 'verb' the user has set, if any
 
-                return false;
-            }
+    common_debug(
+        "FacebookPlugin - Looking to see if $user->nickname ($user->id), "
+        . "Facebook UID: $fbuid has set a verb for Facebook posting..."
+    );
 
-        }
+    $facebook = getFacebook();
+    $verb = trim(
+        $facebook->api_client->data_getUserPreference(
+            FACEBOOK_NOTICE_PREFIX,
+            $fbuid
+        )
+    );
+
+    common_debug("Facebook returned " . var_export($verb, true));
+
+    $text = null;
+
+    if (!empty($verb)) {
+        common_debug("FacebookPlugin - found a verb: $verb");
+        $text = trim($verb) . ' ' . $notice->content;
+    } else {
+        common_debug("FacebookPlugin - no verb found.");
+        $text = $notice->content;
     }
 
-    return true;
-
+    return $text;
 }
 
-function updateProfileBox($facebook, $flink, $notice) {
-    $fbaction = new FacebookAction($output = 'php://output',
-                                   $indent = null, $facebook, $flink);
+function updateProfileBox($facebook, $flink, $notice, $user) {
+
+    $facebook = getFacebook();
+    $fbaction = new FacebookAction(
+        $output = 'php://output',
+        $indent = null,
+        $facebook,
+        $flink
+    );
+
+    $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"
+    );
+
     $fbaction->updateProfileBox($notice);
+
+    common_debug(
+        'FacebookPlugin - finished updating profile box for '
+        . "$user->nickname ($user->id) Facebook UID: $fbuid"
+    );
+
 }
 
 function format_attachments($attachments)