diff --git a/README.txt b/README.txt index e6bab3a..148af07 100644 --- a/README.txt +++ b/README.txt @@ -23,8 +23,8 @@ To upgrade from on D7 version to the next: To install: -- Make sure you have a PHP client from facebook (version < 3.0.0). - The 3.0.0 or higher versions are not supported by this version of Drupal for Facebook. +- Make sure you have a PHP client from facebook (version >= 3.1.1). + The 2.x.y versions are not supported by this version of Drupal for Facebook. Download from http://github.com/facebook/php-sdk. Extract the files, and place them in sites/all/libraries/facebook-php-sdk. @@ -32,10 +32,10 @@ To install: another recognised location (such as sites/all/libraries), providing that the directory is named 'facebook-php-sdk'. - Or, to manually set the location of the php-sdk in any other directory, edit - your settings.php to include a line similar to this (add to the section where - the $conf variable is defined, or the very end of settings.php. And - customize the path as needed.): + Or, to manually set the location of the php-sdk in any other + directory, edit your settings.php to include a line similar to the + one below. Add to the section where the $conf variable is defined, + or the very end of settings.php. And customize the path as needed. $conf['fb_api_file'] = 'sites/all/libraries/facebook-php-sdk/src/facebook.php'; diff --git a/contrib/fb_user_app.module b/contrib/fb_user_app.module index ac1fce3..e57a7c0 100644 --- a/contrib/fb_user_app.module +++ b/contrib/fb_user_app.module @@ -123,11 +123,16 @@ function fb_user_app_user_delete($account) { } /** - * Keep track of when the user has visited the app, and whether they've - * authorized the app or not. + * Keep track of when the user has visited the app. * - * Historically this supported infinite sessions. I believe if this data is - * no longer necessary for the offline access extended permission. + * Historically we could learn a user's ID even if they hadn't authorized + * ("added") the app. No longer the case, so all entries in fb_user_app + * should be for authorized users. + * + * A "signed request" should be fully-formed (have an oauth_token) on canvas + * pages, and on post authorize events (for as long as facebook continues to + * support them). So this tracking will work best for canvas page apps and + * less reliably for connect. */ function fb_user_app_track($fb, $fb_app) { // Coming from a user adding the app or a page adding the app? @@ -139,28 +144,38 @@ function fb_user_app_track($fb, $fb_app) { $fbu = $_REQUEST['fb_sig_page_id']; } + $sr = $fb->getSignedRequest(); + watchdog('fb_user_app', __FUNCTION__ . " signed request is
" . print_r($sr,1) . "
"); // debug - // test if we are tracking only those apps that have been granted offline - // access. - $fb_session = $fb->getSession(); + if (isset($sr['oauth_token'])) { + $access_token = $sr['oauth_token']; + $expires = $sr['expires']; + $fbu = $sr['user_id']; + } + else { + // @TODO: with new SDK, is there any useful tracking info? + return; + } // when 'expires' == 0 app has been granted offline access if ($fb_user_type == 'user' && - $fb_session["expires"] <> 0 && - variable_get(FB_USER_APP_VAR_USERS_THAT_GRANT_OFFLINE, FALSE)) + $expires <> 0 && + variable_get(FB_USER_APP_VAR_USERS_THAT_GRANT_OFFLINE, FALSE)) { + // Note, with new SDK, facebook provides 'expires' date even when user HAS GRANTED offline_access! + // @TODO: find some way to tell whether an access token will actually expire! return; + } // Track this event only if allowed to and only for users, not pages if ((variable_get(FB_USER_APP_VAR_TRACK_USERS, TRUE) && $fb_user_type = "user") || (variable_get(FB_USER_APP_VAR_TRACK_PAGES, TRUE) && $fb_user_type = "page")) { - $access_token = isset($fb_session['access_token']) ? $fb_session['access_token'] : ''; $result1 = db_query("UPDATE {fb_user_app} SET time_access=:time, session_key=:token, session_key_expires=:expires, user_type=:type WHERE apikey=:apikey AND fbu=:fbu", array( ':time' => REQUEST_TIME, ':token' => $access_token, - ':expires' => $fb_session['expires'], + ':expires' => $expires, ':type' => $fb_user_type, - ':apikey' => $fb_app->apikey, + ':apikey' => $fb_app->id, ':fbu' => fb_facebook_user($fb), )); @@ -168,7 +183,7 @@ function fb_user_app_track($fb, $fb_app) { // The row for this user was never inserted, or it was deleted, or the times were the same. $fbu = fb_facebook_user($fb); if ($fbu) { - //First make sure it was not just the same time + // First make sure it was not just the same time $result = db_query("SELECT * FROM {fb_user_app} WHERE apikey=:apikey AND fbu=:fbu", array( ':apikey' => $fb_app->apikey, ':fbu' => $fbu, @@ -184,7 +199,7 @@ function fb_user_app_track($fb, $fb_app) { ':added' => $data['is_app_user'], ':user_type' => $fb_user_type, ':session_key' => $access_token, - ':session_key_expires' => $fb_session['expires'], + ':session_key_expires' => $expires, ':time_access' => REQUEST_TIME, ':proxied_email' => $data['email'] ? $data['email'] : ($data['proxied_email'] ? $data['proxied_email'] : ''), // test accounts will not have ':time_cron' => 0, @@ -212,7 +227,7 @@ function fb_user_app_get_proxied_email($fbu, $fb_app) { $mail = $data->proxied_email; } - if (!$mail) { + if (!isset($mail) || !$mail) { // Ask facebook for info. $fb = fb_api_init($fb_app); $info = fb_users_getInfo(array($fbu), $fb); diff --git a/fb.install b/fb.install index 900c0b8..d9f3468 100644 --- a/fb.install +++ b/fb.install @@ -61,27 +61,26 @@ function fb_requirements($phase) { $fb_lib_path = function_exists('libraries_get_path') ? libraries_get_path('facebook-php-sdk') : 'sites/all/libraries/facebook-php-sdk'; $fb_platform = variable_get(FB_VAR_API_FILE, $fb_lib_path . '/src/facebook.php'); + if (!class_exists('Facebook') && $fb_platform) { + // Attempt to load, if not loaded already. + include($fb_platform); + } + if (class_exists('Facebook')) { $status['value'] = Facebook::VERSION; - if (Facebook::VERSION >= "3") { - $status['description'] = $t('Expected version 2.x.y of Facebook PHP SDK.'); + if (Facebook::VERSION < "3" || Facebook::VERSION > "4") { + $status['description'] = $t('Expected version 3.x.y of Facebook PHP SDK.'); $status['severity'] = REQUIREMENT_ERROR; } else { - $status['description'] = $t('Facebook PHP SDK loaded.'); + $status['description'] = $fb_platform; $status['severity'] = REQUIREMENT_OK; } } - elseif (include($fb_platform)) { // include() better than file_exists(). - $status['description'] = $t('Facebook SDK found at %path', - array('%path' => $fb_platform)); - $status['severity'] = REQUIREMENT_OK; - if ($phase == 'runtime') { - $status['value'] = $t('Found'); - } - } else { - $status['description'] = $t('Facebook client API not found. See modules/fb/README.txt'); + $status['description'] = $t('Facebook client API not found at %path. See modules/fb/README.txt', array( + '%path' => $fb_platform, + )); $status['severity'] = REQUIREMENT_ERROR; if ($phase == 'runtime') { $status['value'] = $t('Not found'); diff --git a/fb.js b/fb.js index 8068f63..c0b63b7 100644 --- a/fb.js +++ b/fb.js @@ -27,10 +27,14 @@ FB_JS = function(){}; * Finish initializing, whether there is an application or not. */ FB_JS.initFinal = function(response) { - var status = {'session' : response.session, 'response': response}; + var status = { + 'session' : response.authResponse, // deprecated + 'auth': response.authResponse, + 'response': response + }; jQuery.event.trigger('fb_init', status); // Trigger event for third-party modules. - FB_JS.sessionChange(response); // This will act only if fbu changed. + FB_JS.authResponseChange(response); // This will act only if fbu changed. FB_JS.eventSubscribe(); @@ -42,7 +46,7 @@ FB_JS.initFinal = function(response) { */ FB_JS.eventSubscribe = function() { // Use FB.Event to detect Connect login/logout. - FB.Event.subscribe('auth.sessionChange', FB_JS.sessionChange); + FB.Event.subscribe('auth.authResponseChange', FB_JS.authResponseChange); // Q: what the heck is "edge.create"? A: the like button was clicked. FB.Event.subscribe('edge.create', FB_JS.edgeCreate); @@ -75,10 +79,11 @@ FB_JS.getUrlVars = function(href) { */ FB_JS.reload = function(destination) { // Determine url hash. - var session = FB.getSession(); + var auth = FB.getAuthResponse(); + var fbhash; - if (session != null) - fbhash = session.sig; // Use sig rather than compute a new hash. + if (auth != null) + fbhash = auth.signedRequest; // Use sig rather than compute a new hash. else fbhash = 0; @@ -132,15 +137,22 @@ FB_JS.reload = function(destination) { }; // Facebook pseudo-event handlers. -FB_JS.sessionChange = function(response) { +FB_JS.authResponseChange = function(response) { + //debugger; if (response.status == 'unknown') { // @TODO can we test if third-party cookies are disabled? } - var status = {'changed': false, 'fbu': null, 'session': response.session, 'response' : response}; + var status = { + 'changed': false, + 'fbu': null, + 'session': response.authResponse, // deprecated, still needed??? + 'auth': response.authResponse, // still needed??? + 'response' : response + }; - if (response.session) { - status.fbu = response.session.uid; + if (response.authResponse) { + status.fbu = response.authResponse.userID; if (Drupal.settings.fb.fbu != status.fbu) { // A user has logged in. status.changed = true; @@ -152,6 +164,7 @@ FB_JS.sessionChange = function(response) { // Sometimes Facebook's invalid cookies are left around. Let's try to clean up their crap. // Can get left behind when third-party cookies disabled. + // @TODO: Still needed with new oauth??? Have cookies been renamed (fbsr_...)??? FB_JS.deleteCookie('fbs_' + FB._apiKey, '/', ''); FB_JS.deleteCookie('fbs_' + Drupal.settings.fb.apikey, '/', ''); } @@ -180,7 +193,7 @@ FB_JS.sessionChangeHandler = function(context, status) { }; if (status.session) { - data.fbu = status.session.uid; + data.fbu = status.session.userID; // Suppress facebook-controlled session. data.fb_session_handoff = true; } @@ -196,7 +209,7 @@ FB_JS.ajaxEvent = function(event_type, request_data) { if (Drupal.settings.fb.ajax_event_url) { // Session data helpful in ajax callbacks. See fb_settings.inc. - request_data.fb_js_session = JSON.stringify(FB.getSession()); + // request_data.fb_js_session = JSON.stringify(FB.getSession()); // FB.getSession() FAILS! REMOVE or REPLACE. if (typeof(Drupal.settings.fb_page_type) != 'undefined') { request_data.fb_js_page_type = Drupal.settings.fb_page_type; } diff --git a/fb.module b/fb.module index ebbeeec..062622d 100644 --- a/fb.module +++ b/fb.module @@ -166,6 +166,7 @@ function fb_init() { 'xfbml' => FALSE, 'status' => FALSE, 'cookie' => variable_get(FB_VAR_USE_COOKIE, TRUE), + 'oauth' => TRUE, ); if ($_fb_app) { @@ -183,43 +184,11 @@ function fb_init() { if ($_fb) { - // Look for session info from several sources. - if ($session = $_fb->getSession()) { - // Learned session from cookie or signed request. - // Below, we store in our $_SESSION, just in case third-party cookies are not enabled. - } - elseif (isset($_REQUEST['fb_js_session'])) { - // Ajax callback via fb.js. - $_fb->setSession(json_decode($_REQUEST['fb_js_session'], TRUE)); - $session = $_fb->getSession(); - } - elseif (isset($_SESSION['fb'][$_fb_app->id]['session']) && arg(0) != 'logout') { - // Use the session previously stored. - $_fb->setSession($_SESSION['fb'][$_fb_app->id]['session']); - } - - // Store session for future use. We'll need it if third-party cookies - // disabled, or we are not using facebook's cookie. - if (isset($session) && variable_get(FB_VAR_USE_SESSION, TRUE)) { - $_SESSION['fb'][$_fb_app->id]['session'] = $session; - } - - // Make javascript work even when third-party cookies disabled. - $fb_init_settings['session'] = $_fb->getSession(); + // @TODO: test this code with third party cookies disabled. - // Sometimes when canvas page is open in one tab, and user logs out of + // @TODO: test if this is still true: Sometimes when canvas page is open in one tab, and user logs out of // facebook in another, the canvas page has bogus session info when // refreshed. Here we attempt to detect and cleanup. - if (isset($session) && ($request = $_fb->getSignedRequest())) { - if (!isset($request['user_id']) || - $request['user_id'] != $session['uid']) { - _fb_logout(); - $_fb->setSession(NULL); - unset($session); - unset($_SESSION['fb'][$_fb_app->id]); - unset($fb_init_settings['session']); - } - } // Give other modules a chance to initialize. fb_invoke(FB_OP_INITIALIZE, array( @@ -228,13 +197,13 @@ function fb_init() { )); // See if the facebook user id is known - if ($fbs = $_fb->getSession()) { + if ($fbu = $_fb->getUser()) { fb_invoke(FB_OP_APP_IS_AUTHORIZED, array( 'fb_app' => $_fb_app, 'fb' => $_fb, - 'fbu' => $_fb->getUser(), + 'fbu' => $fbu, )); - fb_js_settings('fbu', $_fb->getUser()); + fb_js_settings('fbu', $fbu); } else { // Add perms to settings, for calling FB.login(). @@ -387,8 +356,8 @@ function fb_api_init($fb_app) { return NULL; } - if (Facebook::VERSION >= "3") { - $message = 'This version of modules/fb is compatible with Facebook PHP SDK version 2.x, but %version was found. Either upgrade modules/fb or downgrade %fb_platform.'; + if (Facebook::VERSION < "3") { + $message = 'This version of modules/fb is compatible with Facebook PHP SDK version 3.x.y, but %version was found (%fb_platform).'; $args = array('%fb_platform' => $fb_platform, '%version' => Facebook::VERSION); if (user_access('access administration pages')) { drupal_set_message(t($message, $args)); @@ -505,8 +474,7 @@ function fb_get_token($fb = NULL, $fbu = NULL) { $cache_key .= '_' . $fbu; // Get the user access token. if ($fbu == 'me' || $fbu == fb_facebook_user($fb)) { - $session = $fb->getSession(); - $cache[$cache_key] = $session['access_token']; + $cache[$cache_key] = $fb->getAccessToken(); } else { $session_data = fb_invoke(FB_OP_GET_USER_SESSION, array( @@ -639,24 +607,27 @@ function fb_page_alter(&$page) { $output .= "\n"; @@ -740,9 +711,10 @@ function fb_api_check_session($fb) { unset($_SESSION['fb'][$fb->getAppId()]); // Unsetting the javasript fbu can be helpful when third-party cookies disabled. - fb_js_settings('fbu', 0); + //fb_js_settings('fbu', 0); @TODO still needed? helpful? // Might as well try to clean up the mess. + // @TODO: Are facebook cookies renamed (fbsr_...)??? Is this even needed anymore??? if (isset($_COOKIE['fbs_' . $fb->getAppId()])) { setcookie('fbs_' . $fb->getAppId(), '', REQUEST_TIME - 42000, '/'); } @@ -761,9 +733,10 @@ function _fb_logout() { drupal_session_initialize(); // Unsetting the javasript fbu can be helpful when third-party cookies disabled. - fb_js_settings('fbu', 0); + //fb_js_settings('fbu', 0); @TODO still needed?? helpful??? // Clean up facebook cookies. + // @TODO: Are facebook cookies renamed (fbsr_...)??? Is this even needed anymore??? if (isset($GLOBALS['_fb_app'])) { if (isset($_COOKIE['fbs_' . $GLOBALS['_fb_app']->apikey])) { // still needed? setcookie('fbs_' . $GLOBALS['_fb_app']->apikey, '', time() - 42000, '/'); @@ -1031,25 +1004,28 @@ function fb_get_friends($fbu, $fb_app = NULL) { 'query' => $query, )); //dpm($result, "FQL " . $query); // debug + if (is_array($result)) + foreach ($result as $data) { + $items[] = $data['uid2']; + } + + // Facebook's API has the annoying habit of returning an item even if user + // has no friends. We need to clean that up. + if (!$items[0]) + unset($items[0]); + + $cache[$fbu] = $items; } catch (Exception $e) { fb_log_exception($e, t('Failed call to fql.query: !query', array('!query' => $query)), $fb); } - if (is_array($result)) - foreach ($result as $data) { - $items[] = $data['uid2']; - } } - // Facebook's API has the annoying habit of returning an item even if user - // has no friends. We need to clean that up. - if (!$items[0]) - unset($items[0]); - - $cache[$fbu] = $items; } - return $cache[$fbu]; + if (isset($cache[$fbu])) { + return $cache[$fbu]; + } } // Return array of facebook gids diff --git a/fb.theme.inc b/fb.theme.inc index 5b59806..20a5f28 100644 --- a/fb.theme.inc +++ b/fb.theme.inc @@ -21,12 +21,12 @@ function theme_fb_login_button($params) { $options += array( 'attributes' => array(), ); - if (!isset($options['attributes']['perms'])) { + if (!isset($options['attributes']['scope'])) { // Which permissions to prompt for? $perms = array(); drupal_alter('fb_required_perms', $perms); if (count($perms)) { - $options['attributes']['perms'] = implode(',', $perms); + $options['attributes']['scope'] = implode(',', $perms); } } diff --git a/fb_app.install b/fb_app.install index 6af3c34..273a445 100644 --- a/fb_app.install +++ b/fb_app.install @@ -93,9 +93,9 @@ function fb_app_schema() { /** * Change app labels to all lower case. * - * This include some code specific to fb_connect.module (because easier to do here than fb_connect.install). */ function fb_app_update_7301() { + // This include some code specific to fb_connect.module (because easier to do here than fb_connect.install). $ret = array(); $query = db_query("SELECT fba_id, label FROM {fb_app} ORDER BY fba_id"); while ($app = $query->fetchObject()) { diff --git a/fb_connect.js b/fb_connect.js index a5fef5c..8e5ece7 100644 --- a/fb_connect.js +++ b/fb_connect.js @@ -55,10 +55,11 @@ FB_Connect.logoutHandler = function(event) { }); // Facebook's invalid cookies persist if third-party cookies disabled. // Let's try to clean up the mess. + // @TODO: is this still needed with newer oauth SDK??? FB_JS.deleteCookie('fbs_' + FB._apiKey, '/', ''); // app id FB_JS.deleteCookie('fbs_' + Drupal.settings.fb.apikey, '/', ''); // apikey } - if (FB.getSession()) { + if (FB.getUser()) { // @TODO: still needed with newer oauth SDK??? // Facebook needs more time to log us out. (http://drupal.org/node/1164048) Drupal.settings.fb.reload_url = Drupal.settings.fb_connect.front_url; return false; diff --git a/fb_connect.module b/fb_connect.module index e21553e..8d750e6 100644 --- a/fb_connect.module +++ b/fb_connect.module @@ -148,15 +148,12 @@ function _fb_connect_add_js($fb_app, $fb) { // @TODO fb.module should have a helper to make this cleaner. $settings['fb_init_settings']['appId'] = $fb_app->id; - $settings['fb_init_settings']['session'] = $fb->getSession(); fb_js_settings('apikey', $fb_app->apikey); fb_js_settings('fbu', fb_facebook_user($fb)); fb_js_settings('fb_init_settings', $settings['fb_init_settings']); - //$js = drupal_add_js(array('fb' => fb_js_settings()), 'setting'); // fb.module will add settings to footer. } } - } @@ -166,11 +163,11 @@ function _fb_connect_add_js($fb_app, $fb) { function _fb_connect_block_login_defaults() { return array('anon_not_connected' => array( 'title' => t('Facebook Connect'), - 'body' => array('value' => 'Connect'), + 'body' => array('value' => 'Connect'), ), 'user_not_connected' => array( 'title' => t('Facebook Connect'), - 'body' => array('value' => 'Connect'), + 'body' => array('value' => 'Connect'), ), 'connected' => array( 'title' => t('Facebook Connect'), diff --git a/fb_devel.js b/fb_devel.js index d309563..23e2429 100644 --- a/fb_devel.js +++ b/fb_devel.js @@ -15,9 +15,18 @@ FB_Devel.sanityCheck = function() { // before fb.js has a chance to initilize it! To fix: use browser // to view page source, find all \n"; + return $output; +} + + + /** * Provides a page with useful debug info. * @@ -339,12 +356,15 @@ function fb_devel_page() { // TODO: determine whether connect page or canvas. drupal_set_message(t("session name: " . session_name())); - drupal_set_message(t("cookie domain: " . fb_settings(FB_SETTINGS_COOKIE_DOMAIN))); drupal_set_message(t("session id: " . session_id())); + drupal_set_message(t("cookie domain: " . fb_settings(FB_SETTINGS_COOKIE_DOMAIN))); + if (isset($_COOKIE['fbs_' . $_fb_app->apikey])) drupal_set_message(t("fbs_" . $_fb_app->apikey . ": " . $_COOKIE["fbs_" . $_fb_app->apikey])); + drupal_set_message(t("processed link, unprocessed", array('!url' => url('fb/devel')))); drupal_set_message(t("getUser() returns " . $_fb->getUser())); + drupal_set_message(t("getAccessToken() returns " . $_fb->getAccessToken())); drupal_set_message(t("base_url: " . $GLOBALS['base_url'])); drupal_set_message(t("base_path: " . $GLOBALS['base_path'])); @@ -368,7 +388,6 @@ function fb_devel_page() { dpm($_COOKIE, 'cookie'); dpm($_REQUEST, "Request"); //dpm($_fb_app, "fb_app"); - drupal_set_message(t("session_id returns " . session_id())); dpm($_SESSION, "session:"); foreach ($items as $key => $val) { @@ -457,8 +476,15 @@ function fb_devel_tab() { function fb_devel_fbu_page($fbu = NULL) { global $_fb, $_fb_app; if ($fbu) { + // Uses FQL + $info = fb_users_getInfo(array($fbu), $_fb); + $output = "

Debug FQL info about facebook id $fbu ({$info[0]['name']}):

\n"; + $output .= ""; + $output .= "
" . print_r($info[0], 1) . "
"; + + // Use new graph api $info = $_fb->api($fbu, array('metadata' => 1)); - $output = "

Debug info about facebook id $fbu ({$info[name]}):

\n"; + $output .= "

Debug info about facebook id $fbu ({$info['name']}):

\n"; $output .= ""; $output .= "
" . print_r($info, 1) . "
"; @@ -479,7 +505,7 @@ function fb_devel_fbu_page($fbu = NULL) { //dpm($friends, "$fbu/friends returned"); $items = array(); foreach ($friends['data'] as $data) { - $items[] = l($data['name'], "fb/devel/fbu/{$data[id]}"); + $items[] = l($data['name'], "fb/devel/fbu/{$data['id']}"); } if (count($items)) { $output .= "\n

Known friends: