diff --git a/contrib/fb_example.info b/contrib/fb_example.info index 24320c2..687a382 100644 --- a/contrib/fb_example.info +++ b/contrib/fb_example.info @@ -1,8 +1,15 @@ -name = FB Example Customizations -description = (fb_example.module) These hooks will make your site behave more like drupalforfacebook.org. Use this code as an example for your own customizations. -package = Drupal for Facebook - contrib -core = 6.x -dependencies[] = fb -dependencies[] = fb_form -dependencies[] = fb_app -dependencies[] = fb_friend +name = FB Example Customizations +description = (fb_example.module) These hooks will make your site behave more like drupalforfacebook.org. Use this code as an example for your own customizations. +package = Drupal for Facebook - contrib +core = 7.x +dependencies[] = fb +dependencies[] = fb_form +dependencies[] = fb_app +dependencies[] = fb_friend + +; Information added by drupal.org packaging script on 2011-06-23 +version = "7.x-4.x-dev" +core = "7.x" +project = "fb" +datestamp = "1308787966" + diff --git a/contrib/fb_friend.info b/contrib/fb_friend.info index 8ffe311..a93a126 100644 --- a/contrib/fb_friend.info +++ b/contrib/fb_friend.info @@ -1,8 +1,15 @@ -name = FB Friend Features -description = (fb_friend.module) Blocks for showing the current user's friends, sending them requests, etc. -package = Drupal for Facebook - contrib -core = 6.x -dependencies[] = fb -dependencies[] = fb_form -dependencies[] = fb_app - +name = FB Friend Features +description = (fb_friend.module) Blocks for showing the current user's friends, sending them requests, etc. +package = Drupal for Facebook - contrib +core = 7.x +dependencies[] = fb +dependencies[] = fb_form +dependencies[] = fb_app + + +; Information added by drupal.org packaging script on 2011-06-23 +version = "7.x-4.x-dev" +core = "7.x" +project = "fb" +datestamp = "1308787966" + diff --git a/contrib/fb_permission.info b/contrib/fb_permission.info index 167a7f4..faeefb2 100644 --- a/contrib/fb_permission.info +++ b/contrib/fb_permission.info @@ -1,6 +1,13 @@ -name= FB Extended Permissions -description = (fb_permission.module) Host applications that require or support Extended Permissions. -package = Drupal for Facebook - contrib -dependencies[] = fb -dependencies[] = fb_app -core = 6.x +name= FB Extended Permissions +description = (fb_permission.module) Host applications that require or support Extended Permissions. +package = Drupal for Facebook - contrib +dependencies[] = fb +dependencies[] = fb_app +core = 7.x + +; Information added by drupal.org packaging script on 2011-06-23 +version = "7.x-4.x-dev" +core = "7.x" +project = "fb" +datestamp = "1308787966" + diff --git a/contrib/fb_registration.info b/contrib/fb_registration.info index e8417a0..8965b56 100644 --- a/contrib/fb_registration.info +++ b/contrib/fb_registration.info @@ -1,7 +1,14 @@ -name = FB Registration -description = (fb_registration.module) Use the registration feature. -package = Drupal for Facebook - contrib -core = 6.x -dependencies[] = fb -dependencies[] = fb_app - +name = FB Registration +description = (fb_registration.module) Use the registration feature. +package = Drupal for Facebook - contrib +core = 7.x +dependencies[] = fb +dependencies[] = fb_app + + +; Information added by drupal.org packaging script on 2011-06-23 +version = "7.x-4.x-dev" +core = "7.x" +project = "fb" +datestamp = "1308787966" + diff --git a/contrib/fb_rules.info b/contrib/fb_rules.info index e9e3ffe..145dbef 100644 --- a/contrib/fb_rules.info +++ b/contrib/fb_rules.info @@ -1,6 +1,13 @@ -name= FB Rules Integration -description = (fb_rules.module) Provide Facebook events access and interaction through Rules. -package = Drupal for Facebook - contrib -dependencies[] = fb -dependencies[] = rules -core = 6.x +name= FB Rules Integration +description = (fb_rules.module) Provide Facebook events access and interaction through Rules. +package = Drupal for Facebook - contrib +dependencies[] = fb +dependencies[] = rules +core = 7.x + +; Information added by drupal.org packaging script on 2011-06-23 +version = "7.x-4.x-dev" +core = "7.x" +project = "fb" +datestamp = "1308787966" + diff --git a/contrib/fb_test.info b/contrib/fb_test.info index 82ba0a2..f3474c5 100644 --- a/contrib/fb_test.info +++ b/contrib/fb_test.info @@ -1,9 +1,16 @@ -name = FB Testing Helpers -description = (fb_test.module) Create test accounts. -package = Drupal for Facebook - contrib -core = 7.x -dependencies[] = fb -dependencies[] = fb_app - -files[] = fb_test.module - +name = FB Testing Helpers +description = (fb_test.module) Create test accounts. +package = Drupal for Facebook - contrib +core = 7.x +dependencies[] = fb +dependencies[] = fb_app + +files[] = fb_test.module + + +; Information added by drupal.org packaging script on 2011-06-23 +version = "7.x-4.x-dev" +core = "7.x" +project = "fb" +datestamp = "1308787966" + diff --git a/contrib/fb_test.module b/contrib/fb_test.module index 0d10455..1bf2051 100644 --- a/contrib/fb_test.module +++ b/contrib/fb_test.module @@ -1,196 +1,196 @@ - 'Test Accounts', - 'page callback' => 'fb_test_detail_page', - 'page arguments' => array(FB_PATH_ADMIN_APPS_ARGS), - 'access arguments' => array(FB_PERM_ADMINISTER), - 'type' => MENU_LOCAL_TASK, - ); - - return $items; -} - -function fb_test_detail_page($fb_app) { - $output['form1'] = drupal_get_form('fb_test_create_form', $fb_app); - $output['form2'] = drupal_get_form('fb_test_accounts_form', $fb_app); - return $output; -} - -function fb_test_create_form(&$form, &$form_state, $fb_app) { - $form['#fb_app'] = $fb_app; - - // @TODO make this a dropdown, limit to 50 total test accounts. - $form['how_many'] = array( - '#type' => 'textfield', - '#title' => t('Number of test accounts to create'), - ); - - $form['installed'] = array( - '#type' => 'checkbox', - '#title' => t('Installed'), - '#description' => t('If selected, new users have already authorized the application'), - ); - - // Which permissions? - $perms = array(); - drupal_alter('fb_required_perms', $perms); // @TODO pass fb_app to this function. - if (count($perms)) { - $default_perms = implode(',', $perms); - } - - $form['perms'] = array( - '#type' => 'textfield', - '#title' => t('Extended Permission'), - '#description' => t('If app is authorized, users have granted these permissions.'), - '#default_value' => $default_perms, - ); - - $form['submit'] = array( - '#type' => 'submit', - '#value' => t('Create Test Accounts'), - ); - return $form; -} - -function fb_test_create_form_submit($form, &$form_state) { - $values = $form_state['values']; - $fb_app = $form['#fb_app']; - try { - $fb = fb_api_init($fb_app); - for ($i = 0; $i < $values['how_many']; $i++) { - $result = $fb->api($fb_app->id . "/accounts/test-users", 'POST', array( - 'installed' => $values['installed'], - 'permissions' => $values['perms'], - 'access_token' => fb_get_token($fb), - )); - } - drupal_set_message(t('Created %num test accounts.', array('%num' => $values['how_many']))); - } - catch (Exception $e) { - fb_log_exception($e, t('Failed to create test accounts.')); - } -} - -function fb_test_accounts_form($form, &$form_state, $fb_app) { - try { - $form['#fb_app'] = $fb_app; - $fb = fb_api_init($fb_app); - - $result = $fb->api($fb_app->id . "/accounts/test-users", 'GET', array( - 'access_token' => fb_get_token($fb), - )); - - foreach ($result['data'] as $test_account) { - $form['#fb_test_accounts'][$test_account['id']] = $test_account; - - $markup = "$test_account[id] (login)"; - if (isset($test_account['access_token'])) { - $data = $fb->api($test_account['id'], 'GET', array( - 'access_token' => $test_account['access_token'], // Only when account is authorized - )); - if ($data['name']) { - $markup .= " $data[name] (profile) "; - } - } - - $options[$test_account['id']] = $markup; - } - if (!empty($options)) { - $form['checkboxes'] = array( - '#type' => 'checkboxes', - '#options' => $options, - ); - } - - $form['operation'] = array( - '#type' => 'select', - '#title' => t('With selected...'), - '#options' => array( - 'none' => t('please select...'), - 'friends' => t('make friends'), - 'delete' => t('delete account'), - ), - '#description' => t('Use caution, there will be no confirmation page.'), - ); - - $form['submit'] = array( - '#type' => 'submit', - '#value' => t('Submit'), - ); - - $form['header'] = array( - '#type' => "markup", - '#value' => "

Test Accounts

", - '#weight' => -10, - ); - } - catch (Exception $e) { - fb_log_exception($e, t('Failed to access test accounts.')); - } - return $form; -} - -function fb_test_accounts_form_submit($form, &$form_state) { - $values = $form_state['values']; - $fb_app = $form['#fb_app']; - $fb = fb_api_init($fb_app); - - try { - if ($values['operation'] == 'friends') { - foreach ($values['checkboxes'] as $fbu => $selected) { - if ($selected && isset($form['#fb_test_accounts'][$fbu]['access_token'])) { - foreach ($values['checkboxes'] as $fbu2 => $selected2) { - if ($selected2 && ($fbu != $fbu2)) { - try { - $result = $fb->api("$fbu/friends/{$fbu2}", "POST", array( - 'access_token' => $form['#fb_test_accounts'][$fbu]['access_token'], - )); - if ($result) { - drupal_set_message(t("User %fbu is friends with $fbu2.", array( - '%fbu' => $fbu, - '%fbu2' => $fbu2, - ))); - } - } - catch (Exception $e) { - $message = t("Failed to create friendship between %fbu and %fbu2.", array( - '%fbu' => $fbu, - '%fbu2' => $fbu2, - )); - fb_log_exception($e, $message); - } - } - } - } - } - } - elseif ($values['operation'] == 'delete') { - foreach ($values['checkboxes'] as $fbu => $selected) { - if ($selected) { - $result = $fb->api($fbu, 'DELETE', array('access_token' => fb_get_token($fb))); - if ($result) { - drupal_set_message(t('Deleted test account %fbu', array( - '%fbu' => $fbu, - ))); - } - } - } - } - else { - dpm("Operation $values[operation] not implemented.", __FUNCTION__); // debug - } - } - catch (Exception $e) { - fb_log_exception($e, t('Failed to perform operation.')); - } -} + 'Test Accounts', + 'page callback' => 'fb_test_detail_page', + 'page arguments' => array(FB_PATH_ADMIN_APPS_ARGS), + 'access arguments' => array(FB_PERM_ADMINISTER), + 'type' => MENU_LOCAL_TASK, + ); + + return $items; +} + +function fb_test_detail_page($fb_app) { + $output['form1'] = drupal_get_form('fb_test_create_form', $fb_app); + $output['form2'] = drupal_get_form('fb_test_accounts_form', $fb_app); + return $output; +} + +function fb_test_create_form($form, &$form_state, $fb_app) { + $form['#fb_app'] = $fb_app; + + // @TODO make this a dropdown, limit to 50 total test accounts. + $form['how_many'] = array( + '#type' => 'textfield', + '#title' => t('Number of test accounts to create'), + ); + + $form['installed'] = array( + '#type' => 'checkbox', + '#title' => t('Installed'), + '#description' => t('If selected, new users have already authorized the application'), + ); + + // Which permissions? + $perms = array(); + drupal_alter('fb_required_perms', $perms); // @TODO pass fb_app to this function. + if (count($perms)) { + $default_perms = implode(',', $perms); + } + + $form['perms'] = array( + '#type' => 'textfield', + '#title' => t('Extended Permission'), + '#description' => t('If app is authorized, users have granted these permissions.'), + '#default_value' => $default_perms, + ); + + $form['submit'] = array( + '#type' => 'submit', + '#value' => t('Create Test Accounts'), + ); + return $form; +} + +function fb_test_create_form_submit($form, &$form_state) { + $values = $form_state['values']; + $fb_app = $form['#fb_app']; + try { + $fb = fb_api_init($fb_app); + for ($i = 0; $i < $values['how_many']; $i++) { + $result = $fb->api($fb_app->id . "/accounts/test-users", 'POST', array( + 'installed' => $values['installed'], + 'permissions' => $values['perms'], + 'access_token' => fb_get_token($fb), + )); + } + drupal_set_message(t('Created %num test accounts.', array('%num' => $values['how_many']))); + } + catch (Exception $e) { + fb_log_exception($e, t('Failed to create test accounts.')); + } +} + +function fb_test_accounts_form($form, &$form_state, $fb_app) { + try { + $form['#fb_app'] = $fb_app; + $fb = fb_api_init($fb_app); + + $result = $fb->api($fb_app->id . "/accounts/test-users", 'GET', array( + 'access_token' => fb_get_token($fb), + )); + + foreach ($result['data'] as $test_account) { + $form['#fb_test_accounts'][$test_account['id']] = $test_account; + + $markup = "$test_account[id] (login)"; + if (isset($test_account['access_token'])) { + $data = $fb->api($test_account['id'], 'GET', array( + 'access_token' => $test_account['access_token'], // Only when account is authorized + )); + if ($data['name']) { + $markup .= " $data[name] (profile) "; + } + } + + $options[$test_account['id']] = $markup; + } + if (!empty($options)) { + $form['checkboxes'] = array( + '#type' => 'checkboxes', + '#options' => $options, + ); + } + + $form['operation'] = array( + '#type' => 'select', + '#title' => t('With selected...'), + '#options' => array( + 'none' => t('please select...'), + 'friends' => t('make friends'), + 'delete' => t('delete account'), + ), + '#description' => t('Use caution, there will be no confirmation page.'), + ); + + $form['submit'] = array( + '#type' => 'submit', + '#value' => t('Submit'), + ); + + $form['header'] = array( + '#type' => "markup", + '#value' => "

Test Accounts

", + '#weight' => -10, + ); + } + catch (Exception $e) { + fb_log_exception($e, t('Failed to access test accounts.')); + } + return $form; +} + +function fb_test_accounts_form_submit($form, &$form_state) { + $values = $form_state['values']; + $fb_app = $form['#fb_app']; + $fb = fb_api_init($fb_app); + + try { + if ($values['operation'] == 'friends') { + foreach ($values['checkboxes'] as $fbu => $selected) { + if ($selected && isset($form['#fb_test_accounts'][$fbu]['access_token'])) { + foreach ($values['checkboxes'] as $fbu2 => $selected2) { + if ($selected2 && ($fbu != $fbu2)) { + try { + $result = $fb->api("$fbu/friends/{$fbu2}", "POST", array( + 'access_token' => $form['#fb_test_accounts'][$fbu]['access_token'], + )); + if ($result) { + drupal_set_message(t("User %fbu is friends with $fbu2.", array( + '%fbu' => $fbu, + '%fbu2' => $fbu2, + ))); + } + } + catch (Exception $e) { + $message = t("Failed to create friendship between %fbu and %fbu2.", array( + '%fbu' => $fbu, + '%fbu2' => $fbu2, + )); + fb_log_exception($e, $message); + } + } + } + } + } + } + elseif ($values['operation'] == 'delete') { + foreach ($values['checkboxes'] as $fbu => $selected) { + if ($selected) { + $result = $fb->api($fbu, 'DELETE', array('access_token' => fb_get_token($fb))); + if ($result) { + drupal_set_message(t('Deleted test account %fbu', array( + '%fbu' => $fbu, + ))); + } + } + } + } + else { + dpm("Operation $values[operation] not implemented.", __FUNCTION__); // debug + } + } + catch (Exception $e) { + fb_log_exception($e, t('Failed to perform operation.')); + } +} diff --git a/contrib/fb_user_app.info b/contrib/fb_user_app.info index 80d6ce5..9967305 100644 --- a/contrib/fb_user_app.info +++ b/contrib/fb_user_app.info @@ -1,9 +1,16 @@ -name= FB User Tracking -description = (fb_user_app.module) Keep records of users most recent visits to your apps. Note that this adds some overhead to each app visit. -package = Drupal for Facebook - contrib -configure = admin/structure/fb/fb_user_app -core = 7.x -dependencies[] = fb -dependencies[] = fb_app - -files[] = fb_user_app.module +name= FB User Tracking +description = (fb_user_app.module) Keep records of users most recent visits to your apps. Note that this adds some overhead to each app visit. +package = Drupal for Facebook - contrib +configure = admin/structure/fb/fb_user_app +core = 7.x +dependencies[] = fb +dependencies[] = fb_app + +files[] = fb_user_app.module + +; Information added by drupal.org packaging script on 2011-06-23 +version = "7.x-4.x-dev" +core = "7.x" +project = "fb" +datestamp = "1308787966" + diff --git a/contrib/fb_user_app.module b/contrib/fb_user_app.module index ac1fce3..09a5000 100644 --- a/contrib/fb_user_app.module +++ b/contrib/fb_user_app.module @@ -1,231 +1,231 @@ - 'Tracking', - 'description' => 'Settings that track statistics in Drupal for Facebook', - 'page callback' => 'drupal_get_form', - 'page arguments' => array('fb_user_app_admin_settings'), - 'access arguments' => array(FB_PERM_ADMINISTER), - 'file' => 'fb_user_app.admin.inc', - 'type' => MENU_LOCAL_TASK, - ); - - return $items; -} - -/** - * Implementation of hook_fb() - */ -function fb_user_app_fb($op, $data, &$return) { - $fb_app = isset($data['fb_app']) ? $data['fb_app'] : NULL; - $fb = isset($data['fb']) ? $data['fb'] : NULL; - - global $user; - - if ($op == FB_OP_APP_IS_AUTHORIZED && - variable_get(FB_USER_APP_VAR_TRACK_EVERY_PAGE, FALSE)) { - - // This hook is called on every page request, if the user has authorized - // the app/page and permission has been granted in the settings. We used - // to create accounts and maps here. That code is now - // in FB_OP_AJAX_EVENT, because it turns out this hook is invoked even on - // page not found and access denied pages. - - fb_user_app_track($fb, $fb_app, $user); - } - elseif ($op == FB_APP_OP_EVENT) { - - // Facebook has notified us of some event. - // We handle some of the events here. - $event_type = $data['event_type']; - - // Ensure fb_user_app table accurately reflects whether user has authorized. - if ($event_type == FB_APP_EVENT_POST_AUTHORIZE) { - // Track new facebook user, $GLOBAL['user'] not valid during post-authorize. - fb_user_app_track($fb, $fb_app); - } - elseif ($event_type == FB_APP_EVENT_POST_REMOVE) { - $fbu = fb_settings(FB_SETTINGS_FBU); - // User has removed the app from their account. - db_query("DELETE FROM {fb_user_app} WHERE apikey=:apikey AND fbu=:fbu", array( - ':apikey' => $fb_app->apikey, - ':fbu' => $fbu, - )); - } - } - elseif ($op == FB_OP_GET_USER_SESSION) { - - // The fb module is asking for session login information. For example, to - // log in as the user when not on a canvas page. This module may be able - // to provide it, depending on whether the user has logged in, and whether - // the session has expired. - $fbu = $data['fbu']; - $result = db_query("SELECT * FROM {fb_user_app} WHERE apikey = :apikey and fbu = :fbu", array( - ':apikey' => $fb_app->apikey, - ':fbu' => $fbu, - )); - $data = $result->fetchObject(); - - if ($data && $data->session_key) - // Return array with FB id and apikey. - $return = array( - 'fbu' => $data->fbu, - 'access_token' => $data->session_key, - 'expires' => $data->session_key_expires, - ); - } - elseif ($op == FB_OP_AJAX_EVENT) { // handle internal login - // @TODO - global user is not correct here. - - // fb.js has notified us of an event via AJAX. Not the same as facebook event callback above. - if ($data['event_type'] == 'session_change' && isset($data['event_data']['fbu'])) { - // A user has logged in. - fb_user_app_track($fb, $fb_app, $user); - } - } -} - -/** - * Implements hook_user_delete(). - * - * @TODO confirm there is no race condition between this module and fb_user. - * That is, during delete, does fb_get_fbu() still work? - */ -function fb_user_app_user_delete($account) { - // Given the uid, fetch the fbu so that we can delete - $fbu = fb_get_fbu($account->uid); - db_query('DELETE FROM {fb_user_app} WHERE fbu=:fbu', array( - ':fbu' => $fbu, - )); -} - -/** - * Keep track of when the user has visited the app, and whether they've - * authorized the app or not. - * - * Historically this supported infinite sessions. I believe if this data is - * no longer necessary for the offline access extended permission. - */ -function fb_user_app_track($fb, $fb_app) { - // Coming from a user adding the app or a page adding the app? - $fb_user_type = "user"; - $fbu = fb_facebook_user($fb); - if (array_key_exists('fb_sig_page_added', $_REQUEST)) { - // It's a post-authorize event for app added to page. - $fb_user_type = "page"; - $fbu = $_REQUEST['fb_sig_page_id']; - } - - - // test if we are tracking only those apps that have been granted offline - // access. - $fb_session = $fb->getSession(); - - // 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)) - 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'], - ':type' => $fb_user_type, - ':apikey' => $fb_app->apikey, - ':fbu' => fb_facebook_user($fb), - )); - - if ($result1 && $result1->rowCount() == 0) { - // 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 - $result = db_query("SELECT * FROM {fb_user_app} WHERE apikey=:apikey AND fbu=:fbu", array( - ':apikey' => $fb_app->apikey, - ':fbu' => $fbu, - )); - if (!$result->fetchObject()) { - //This row does not exist, even with the same time. Insert now - list($data) = fb_fql_query($fb, "SELECT name, is_app_user, email, proxied_email FROM user WHERE uid=$fbu", array('access_Token' => $fb_session_key)); - //watchdog('fb_user_app', "fb user data
" . print_r($data, 1) . '
'); - $fb_user_type = "user"; - $result = db_query("INSERT INTO {fb_user_app} (apikey, fbu, added, user_type, session_key, session_key_expires, time_access, proxied_email, time_cron) VALUES (:apikey, :fbu, :added, :user_type, :session_key, :session_key_expires, :time_access, :proxied_email, :time_cron)", array( - ':apikey' => $fb_app->apikey, - ':fbu' => $fbu, - ':added' => $data['is_app_user'], - ':user_type' => $fb_user_type, - ':session_key' => $access_token, - ':session_key_expires' => $fb_session['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, - )); - } - } - } - if (FALSE && $result === FALSE) { // XXX upgrade to D7??? - watchdog('fb_user_app', "Failed to update fb_user_app table.", array(), WATCHDOG_ERROR); - } - } -} - -/** - * Learn the user's proxied email address. - * - */ -function fb_user_app_get_proxied_email($fbu, $fb_app) { - // Try to learn from local database - $result = db_query("SELECT * FROM {fb_user_app} WHERE apikey=:apikey AND fbu=:fbu", array( - ':apikey' => $fb_app->apikey, - ':fbu' => $fbu, - )); - if ($data = $result->fetchObject()) { - $mail = $data->proxied_email; - } - - if (!$mail) { - // Ask facebook for info. - $fb = fb_api_init($fb_app); - $info = fb_users_getInfo(array($fbu), $fb); - $data = $info[0]; - $mail = $data['proxied_email']; - if ($mail && variable_get(FB_USER_APP_VAR_TRACK_USERS, TRUE)) { - // Store locally. - $result = db_query("UPDATE {fb_user_app} SET proxied_email=:mail WHERE apikey=:apikey AND fbu=:fbu", array( - ':mail' => $mail, - ':apikey' => $fb_app->apikey, - ':fbu' => $fbu, - )); - } - } - return $mail; -} + 'Tracking', + 'description' => 'Settings that track statistics in Drupal for Facebook', + 'page callback' => 'drupal_get_form', + 'page arguments' => array('fb_user_app_admin_settings'), + 'access arguments' => array(FB_PERM_ADMINISTER), + 'file' => 'fb_user_app.admin.inc', + 'type' => MENU_LOCAL_TASK, + ); + + return $items; +} + +/** + * Implementation of hook_fb() + */ +function fb_user_app_fb($op, $data, &$return) { + $fb_app = isset($data['fb_app']) ? $data['fb_app'] : NULL; + $fb = isset($data['fb']) ? $data['fb'] : NULL; + + global $user; + + if ($op == FB_OP_APP_IS_AUTHORIZED && + variable_get(FB_USER_APP_VAR_TRACK_EVERY_PAGE, FALSE)) { + + // This hook is called on every page request, if the user has authorized + // the app/page and permission has been granted in the settings. We used + // to create accounts and maps here. That code is now + // in FB_OP_AJAX_EVENT, because it turns out this hook is invoked even on + // page not found and access denied pages. + + fb_user_app_track($fb, $fb_app, $user); + } + elseif ($op == FB_APP_OP_EVENT) { + + // Facebook has notified us of some event. + // We handle some of the events here. + $event_type = $data['event_type']; + + // Ensure fb_user_app table accurately reflects whether user has authorized. + if ($event_type == FB_APP_EVENT_POST_AUTHORIZE) { + // Track new facebook user, $GLOBAL['user'] not valid during post-authorize. + fb_user_app_track($fb, $fb_app); + } + elseif ($event_type == FB_APP_EVENT_POST_REMOVE) { + $fbu = fb_settings(FB_SETTINGS_FBU); + // User has removed the app from their account. + db_query("DELETE FROM {fb_user_app} WHERE apikey=:apikey AND fbu=:fbu", array( + ':apikey' => $fb_app->apikey, + ':fbu' => $fbu, + )); + } + } + elseif ($op == FB_OP_GET_USER_SESSION) { + + // The fb module is asking for session login information. For example, to + // log in as the user when not on a canvas page. This module may be able + // to provide it, depending on whether the user has logged in, and whether + // the session has expired. + $fbu = $data['fbu']; + $result = db_query("SELECT * FROM {fb_user_app} WHERE apikey = :apikey and fbu = :fbu", array( + ':apikey' => $fb_app->apikey, + ':fbu' => $fbu, + )); + $data = $result->fetchObject(); + + if ($data && $data->session_key) + // Return array with FB id and apikey. + $return = array( + 'fbu' => $data->fbu, + 'access_token' => $data->session_key, + 'expires' => $data->session_key_expires, + ); + } + elseif ($op == FB_OP_AJAX_EVENT) { // handle internal login + // @TODO - global user is not correct here. + + // fb.js has notified us of an event via AJAX. Not the same as facebook event callback above. + if ($data['event_type'] == 'session_change' && isset($data['event_data']['fbu'])) { + // A user has logged in. + fb_user_app_track($fb, $fb_app, $user); + } + } +} + +/** + * Implements hook_user_delete(). + * + * @TODO confirm there is no race condition between this module and fb_user. + * That is, during delete, does fb_get_fbu() still work? + */ +function fb_user_app_user_delete($account) { + // Given the uid, fetch the fbu so that we can delete + $fbu = fb_get_fbu($account->uid); + db_query('DELETE FROM {fb_user_app} WHERE fbu=:fbu', array( + ':fbu' => $fbu, + )); +} + +/** + * Keep track of when the user has visited the app, and whether they've + * authorized the app or not. + * + * Historically this supported infinite sessions. I believe if this data is + * no longer necessary for the offline access extended permission. + */ +function fb_user_app_track($fb, $fb_app) { + // Coming from a user adding the app or a page adding the app? + $fb_user_type = "user"; + $fbu = fb_facebook_user($fb); + if (array_key_exists('fb_sig_page_added', $_REQUEST)) { + // It's a post-authorize event for app added to page. + $fb_user_type = "page"; + $fbu = $_REQUEST['fb_sig_page_id']; + } + + + // test if we are tracking only those apps that have been granted offline + // access. Not sure what to do here ?? - NB + $fb_session = $fb->getAccessToken(); + + // 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)) + 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'], + ':type' => $fb_user_type, + ':apikey' => $fb_app->apikey, + ':fbu' => fb_facebook_user($fb), + )); + + if ($result1 && $result1->rowCount() == 0) { + // 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 + $result = db_query("SELECT * FROM {fb_user_app} WHERE apikey=:apikey AND fbu=:fbu", array( + ':apikey' => $fb_app->apikey, + ':fbu' => $fbu, + )); + if (!$result->fetchObject()) { + //This row does not exist, even with the same time. Insert now + list($data) = fb_fql_query($fb, "SELECT name, is_app_user, email, proxied_email FROM user WHERE uid=$fbu", array('access_Token' => $fb_session_key)); + //watchdog('fb_user_app', "fb user data
" . print_r($data, 1) . '
'); + $fb_user_type = "user"; + $result = db_query("INSERT INTO {fb_user_app} (apikey, fbu, added, user_type, session_key, session_key_expires, time_access, proxied_email, time_cron) VALUES (:apikey, :fbu, :added, :user_type, :session_key, :session_key_expires, :time_access, :proxied_email, :time_cron)", array( + ':apikey' => $fb_app->apikey, + ':fbu' => $fbu, + ':added' => $data['is_app_user'], + ':user_type' => $fb_user_type, + ':session_key' => $access_token, + ':session_key_expires' => $fb_session['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, + )); + } + } + } + if (FALSE && $result === FALSE) { // XXX upgrade to D7??? + watchdog('fb_user_app', "Failed to update fb_user_app table.", array(), WATCHDOG_ERROR); + } + } +} + +/** + * Learn the user's proxied email address. + * + */ +function fb_user_app_get_proxied_email($fbu, $fb_app) { + // Try to learn from local database + $result = db_query("SELECT * FROM {fb_user_app} WHERE apikey=:apikey AND fbu=:fbu", array( + ':apikey' => $fb_app->apikey, + ':fbu' => $fbu, + )); + if ($data = $result->fetchObject()) { + $mail = $data->proxied_email; + } + + if (!$mail) { + // Ask facebook for info. + $fb = fb_api_init($fb_app); + $info = fb_users_getInfo(array($fbu), $fb); + $data = $info[0]; + $mail = $data['proxied_email']; + if ($mail && variable_get(FB_USER_APP_VAR_TRACK_USERS, TRUE)) { + // Store locally. + $result = db_query("UPDATE {fb_user_app} SET proxied_email=:mail WHERE apikey=:apikey AND fbu=:fbu", array( + ':mail' => $mail, + ':apikey' => $fb_app->apikey, + ':fbu' => $fbu, + )); + } + } + return $mail; +} diff --git a/fb.info b/fb.info index 90fa394..e27240e 100644 --- a/fb.info +++ b/fb.info @@ -1,13 +1,19 @@ -name = Facebook API -description = (fb.module) Initializes facebook's PHP and Javascript client libraries. Enables FBML Like buttons and other social plugins. (See modules/fb/README.txt.) - -package = Drupal for Facebook -core = 7.x - -files[] = fb.module -files[] = fb.install -files[] = fb.admin.inc -files[] = fb.theme.inc -files[] = fb_url_rewrite.inc - -scripts[] = fb.js \ No newline at end of file +name = Facebook API +description = (fb.module) Initializes facebook's PHP and Javascript client libraries. Enables FBML Like buttons and other social plugins. (See modules/fb/README.txt.) + +package = Drupal for Facebook +core = 7.x + +files[] = fb.module +files[] = fb.install +files[] = fb.admin.inc +files[] = fb.theme.inc +files[] = fb_url_rewrite.inc + +scripts[] = fb.js +; Information added by drupal.org packaging script on 2011-06-23 +version = "7.x-4.x-dev" +core = "7.x" +project = "fb" +datestamp = "1308787966" + diff --git a/fb.module b/fb.module index b437bbf..49a8e19 100644 --- a/fb.module +++ b/fb.module @@ -1,1567 +1,1567 @@ - TRUE, - ))); - - // Data structure to pass to FB.init(); - $fb_init_settings = array( - 'xfbml' => FALSE, - 'status' => FALSE, - 'cookie' => variable_get(FB_VAR_USE_COOKIE, TRUE), - ); - - if ($_fb_app) { - // An App is configured. - - // Javascript settings needed by fb.js. - fb_js_settings('apikey', $_fb_app->apikey); - fb_js_settings('label', $_fb_app->label); - fb_js_settings('page_type', fb_settings(FB_SETTINGS_TYPE)); // canvas or connect. - $fb_init_settings['apiKey'] = $_fb_app->apikey; - $fb_init_settings['appId'] = $_fb_app->id; - - // Initialize the PHP API. - $_fb = fb_api_init($_fb_app); - - 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->apikey]['session']) && arg(0) != 'logout') { - // Use the session previously stored. - $_fb->setSession($_SESSION['fb'][$_fb_app->apikey]['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->apikey]['session'] = $session; - } - - // Make javascript work even when third-party cookies disabled. - $fb_init_settings['session'] = $_fb->getSession(); - - // 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->apikey]); - unset($fb_init_settings['session']); - } - } - - // Give other modules a chance to initialize. - fb_invoke(FB_OP_INITIALIZE, array( - 'fb_app' => $_fb_app, - 'fb' => $_fb, - )); - - // See if the facebook user id is known - if ($fbs = $_fb->getSession()) { - fb_invoke(FB_OP_APP_IS_AUTHORIZED, array( - 'fb_app' => $_fb_app, - 'fb' => $_fb, - 'fbu' => $_fb->getUser(), - )); - fb_js_settings('fbu', $_fb->getUser()); - } - else { - // Add perms to settings, for calling FB.login(). - $perms = array(); - drupal_alter('fb_required_perms', $perms); - fb_js_settings('perms', implode(',', $perms)); - } - } - else - watchdog('fb', "URL indicates a facebook app, but could not initialize Facebook", array(), WATCHDOG_ERROR); - - if (isset($_REQUEST['destination'])) { - $destination = $_REQUEST['destination']; - } - elseif (current_path()) { - $destination = current_path(); - } - else { - $destination = ''; - } - if (fb_is_canvas()) { - $destination = fb_scrub_urls($destination); // Needed? - } - - //Stripping the fragment out to be tacked on during the javascript redirect - if (strpos($destination, '#') !== FALSE) { - list($destination, $fragment) = explode('#', $destination, 2); - fb_js_settings('reload_url_fragment', $fragment); - } - - fb_js_settings('reload_url', url($destination, array( - 'absolute' => TRUE, - 'fb_canvas' => fb_is_canvas(), - 'language' => (object) array('prefix' => NULL, 'language' => NULL), // http://drupal.org/node/1000452 - ))); - - } - - if ($channel = variable_get(FB_VAR_JS_CHANNEL, TRUE)) { - if (!is_string($channel)) { - $channel = url('fb/channel', array('absolute' => TRUE, 'fb_url_alter' => FALSE)); - } - $fb_init_settings['channelUrl'] = $channel; - } - - fb_js_settings('fb_init_settings', $fb_init_settings); - - - // Allow third-parties to act, even if we did not initialize $_fb. - fb_invoke(FB_OP_POST_INIT, array('fb_app' => $_fb_app, - 'fb' => $_fb)); - - fb_js_settings('controls', implode(',', fb_controls())); - - if (!fb_js_settings('js_sdk_url')) { - if (isset($_SESSION['fb_locale']) && - variable_get(FB_VAR_LANGUAGE_OVERRIDE, 'override')) { - // @TODO - get locale from signed request. It appears to contain it now. - $fb_lang = $_SESSION['fb_locale']; - } - else { - $user_language = user_preferred_language($GLOBALS['user']); - $fb_lang = variable_get('fb_language_' . $user_language->language, 'en_US'); - } - - $js_sdk = fb_protocol() . "://connect.facebook.net/$fb_lang/all.js"; - fb_js_settings('js_sdk_url', variable_get(FB_VAR_JS_SDK, $js_sdk)); - } - - // Add our module's javascript. - drupal_add_js(drupal_get_path('module', 'fb') . '/fb.js', array( - 'type' => 'file', - 'scope' => 'header', - 'group' => JS_LIBRARY, - )); - - // See also fb_page_alter(), where we initialize facebook's SDK. -} - -/** - * Implements hook_rdf_namespaces(). - * Adds the xmlns:fb="http://www.facebook.com/2008/fbml" attribute to html tag. - */ -function fb_rdf_namespaces() { - return array( - 'fb' => 'http://www.facebook.com/2008/fbml', - ); -} - -/** - * Helper to get the configured variables. - * - * Adds the javascript setting with the supplied key/value. This function - * merely keeps track of the settings and writes them as late as possible. - * Currently, in the fb_footer() function. There has been a lot of - * experimentation as to the best place to initialize the facebook javascript - * SDK. The footer appears to be the best place because we may not know all - * settings until well after hook_init(). - * - * @param $key - * The javascript setting name. If the key is null then nothing is modified and the settings are returned. - * @param $value - * The value of the javascript setting. If the key is not NULL by the value is the setting is removed - * @return - * The associative array containing the current fb javascript settings - */ -function fb_js_settings($key = NULL, $value = NULL) { - $fb_js_settings = &drupal_static(__FUNCTION__); - - if (isset($key) && isset($value)) { - $fb_js_settings[$key] = $value; - return $value; - } - elseif (isset($key)) { - return isset($fb_js_settings[$key]) ? $fb_js_settings[$key] : NULL; - } - else { - return $fb_js_settings; - } -} - - -/** - * Include and initialize Facebook's PHP SDK. - */ -function fb_api_init($fb_app) { - $cache = &drupal_static(__FUNCTION__); - // This helps with uncaught exceptions. However, it should be configurable - // or at least not overwrite previously declared handler. - set_exception_handler('fb_handle_exception'); - - if (isset($cache[$fb_app->apikey])) { - return $cache[$fb_app->apikey]; - } - - // Find Facebook's PHP SDK. Use libraries API if enabled. - $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'); - - try { - if (!class_exists('Facebook') && !include($fb_platform)) { - $message = t('Failed to find the Facebook client libraries at %filename. Read the !readme and follow the instructions carefully.', array( - '!drupal_for_facebook' => l(t('Drupal for Facebook'), 'http://drupal.org/project/fb'), - // This link should work with clean URLs disabled. - '!readme' => 'README.txt', - '%filename' => $filename, - )); - drupal_set_message($message, 'error'); - watchdog('fb', $message); - return NULL; - } - - // We don't have a cached resource for this app, so we're going to create one. - $fb = new Facebook(array( - 'appId' => $fb_app->id, - 'secret' => isset($fb_app->secret) ? $fb_app->secret : NULL, - 'cookie' => variable_get(FB_VAR_USE_COOKIE, TRUE), - )); - - // Some servers need these settings. - if (variable_get(FB_VAR_CURL_NOVERIFY, TRUE)) { - Facebook::$CURL_OPTS[CURLOPT_SSL_VERIFYPEER] = FALSE; - Facebook::$CURL_OPTS[CURLOPT_SSL_VERIFYHOST] = FALSE; - //Facebook::$CURL_OPTS[CURLOPT_VERBOSE] = 1; // debug - } - - // Cache the result, in case we're called again. - $cache[$fb_app->apikey] = $fb; - - return $fb; - } - catch (Exception $e) { - fb_log_exception($e, t('Failed to construct Facebook client API.')); - } -} - -/** - * Wrapper function for fb_api_init. This helps for functions that should - * work whether or not we are on a canvas page. For canvas pages, the active - * fb object is used. For non-canvas pages, it will initialize the API using - * an infinite session, if configured. - * - * @param $fb_app Note this is ignored on canvas pages. - * - * This is for internal use. Third party modules use fb_api_init(). - */ -function _fb_api_init($fb_app = NULL) { - $fb = $GLOBALS['_fb']; // Default to active app on canvas pages - if (!$fb && $fb_app) - // Otherwise, log into facebook api. - $fb = fb_api_init($fb_app, FB_FBU_ANY); - - if (!$fb) { - watchdog('fb', '%function unable to initialize Facebook API.', - array('%function' => '_fb_api_init()'), WATCHDOG_ERROR); - return; - } - else - return $fb; -} - - -/** - * Helper function to get the most commonly used values. In your custom - * module, call extract(fb_vars()); to set $fb_app, $fb, and $fbu. - */ -function fb_vars() { - return array( - 'fb' => $GLOBALS['_fb'], - 'fb_app' => $GLOBALS['_fb_app'], - 'fbu' => fb_facebook_user(), - ); -} - - -/** - * Helper to get the tokens needed to accss facebook's API. - * - * You would think that facebook's SDK would provide basic functions like this. - * - * @param $fb - * Get the token for this API instance. If NULL, use the global $_fb. - * - * @param $fbu - * Get the user-specific token. If NULL, get the application token. - */ -function fb_get_token($fb = NULL, $fbu = NULL) { - $cache = &drupal_static(__FUNCTION__); - if (!isset($cache)) - $cache = array(); - - if (!$fb) { - $fb = $GLOBALS['_fb']; - } - if (!$fb) { - return; - } - - $app_id = $fb->getAppId(); - $cache_key = $app_id; - - // @TODO if both fb and fbu are NULL, it might be better performance to use the current user's token, if available. - - if (!$fbu) { - // Get the application token. - if (!isset($cache[$cache_key])) { - $path = "https://graph.facebook.com/oauth/access_token?client_id=" . $app_id . "&client_secret=" . $fb->getApiSecret() . "&type=client_cred"; - $http = drupal_http_request($path); - if ($http->code == 200 && isset($http->data)) { - $data = explode('=', $http->data); - $token = $data[1]; - if ($token) - $cache[$cache_key] = $token; - } - } - } - else { - $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']; - } - else { - $session_data = fb_invoke(FB_OP_GET_USER_SESSION, array( - 'fb' => $fb, - 'fb_app' => fb_get_app(array('id' => $app_id)), - 'fbu' => $fbu, - ), array()); - if (count($session_data)) { - $cache[$cache_key] = $session_data['access_token']; - } - } - } - return isset($cache[$cache_key]) ? $cache[$cache_key] : NULL; -} - -/** - * This helper original written because facebook's $fb->api() function was - * very buggy. I'm not sure this is still needed. On the other hand, a - * future version of modules/fb might use this instead of faceobok's PHP SDK, - * eliminating the need for it entirely. - */ -function fb_call_method($fb, $method, $params = array()) { - $params['access_token'] = fb_get_token($fb); - $params['format'] = 'json-strings'; - - // Here's how to create a url that conforms to standards: - $url = url("https://api.facebook.com/method/{$method}", array( - 'query' => $params, - )); - // If facebook gives errors like "Invalid OAuth 2.0 Access Token 190/Unable to get application prop" it might be necessary to uncomment the urldecode below. - // http://forum.developers.facebook.net/viewtopic.php?id=76228 - // $url = rawurldecode($url); - - $http = drupal_http_request($url); - - if (!isset($http->error) && isset($http->data)) { - $data = json_decode($http->data, TRUE); - // Yes, it's double encoded. At least sometimes. - if (is_string($data)) { - $data = json_decode($data, TRUE); - } - if (is_array($data)) { - if (isset($data['error_code'])) { - throw new FacebookApiException($data); - } - } - elseif ($http->data == 'true' || $http->code == 200) { - // No problems. - } - else { - // Never reach this??? - if (function_exists('dpm')) dpm($http, __FUNCTION__ . " unexpected result from $url"); // XXX - } - return $data; - } - else { - // Should we throw FacebookApiException, or plain old exception? - throw new FacebookApiException( - array( - 'error_msg' => t('fb_call_method failed calling !method. !detail', array( - '!method' => $method, - '!detail' => $http->error, - )), - 'error_code' => $http->code, - )); - } -} - -/** - * Helper function for fql queries. - * - * Use $params to pass a session_key, when needed. - */ -function fb_fql_query($fb, $query, $params = array()) { - $params['query'] = $query; - //$result = fb_call_method($fb, 'fql.query', $params); - $params['method'] = 'fql.query'; - $result = $fb->api($params); - - return $result; -} - - -/** - * Implements hook_page_alter(). - * Can alter the $page['page_bottom'] hidden region here. - */ -function fb_page_alter(&$page) { - global $_fb, $_fb_app; - - // This element recommended by facebook. http://developers.facebook.com/docs/reference/javascript/ - $output = "
\n"; - - $settings = fb_js_settings(); - - $output .= "\n"; - - $page['page_bottom']['fb'] = array( - '#type' => 'markup', - '#markup' => $output, - ); -} - -/** - * Is the current request a canvas page? - */ -function fb_is_canvas() { - if (fb_is_tab()) { - return FALSE; - } - elseif (fb_settings(FB_SETTINGS_CB)) { - // Using fb_url_rewrite. - return TRUE; - } - elseif (fb_settings(FB_SETTINGS_TYPE) == FB_SETTINGS_TYPE_CANVAS) { - // No rewrite, but fb_settings.inc has detected type. - return TRUE; - } - return FALSE; -} - -/** - * Is the current page a profile tab. - * - * Only works when "Canvas Session Parameter" is disabled. - */ -function fb_is_tab() { - global $_fb; - - if (fb_settings(FB_SETTINGS_TYPE) == FB_SETTINGS_TYPE_PAGE_TAB) { - return TRUE; - } - elseif (fb_settings(FB_SETTINGS_TYPE) == FB_SETTINGS_TYPE_PROFILE) { // deprecated FBML tab - return TRUE; - } - elseif (isset($_REQUEST['fb_sig_in_profile_tab']) && - $_REQUEST['fb_sig_in_profile_tab']) { // deprecated ancient history - // Old way, no migrations enabled. - return TRUE; - } - return FALSE; -} - - -/** - * Sometimes calls to fb_api_init succeed, but calls to the client api - * will fail because cookies are obsolete or what have you. This - * function makes a call to facebook to test the session. Expensive, - * so use only when necessary. - * - */ -function fb_api_check_session($fb) { - $success = FALSE; - try { - $me = $fb->api('me'); - - // Store the locale if set. - if (isset($me['locale'])) { - $_SESSION['fb_locale'] = $me['locale']; - } - - // Does not matter what is returned, as long as exception is not thrown. - $success = TRUE; - } - catch (Exception $e) { - if (fb_verbose()) { - watchdog('fb', 'fb_api_check_session failed. Possible attempt to spoof a facebook session!'); - //watchdog('fb', print_r($fb->getSession(), 1)); - } - $success = FALSE; - if (fb_verbose()) { - fb_log_exception($e, t("fb_api_check_session failed.")); - } - - unset($_SESSION['fb'][$fb->getAppId()]); - // Unsetting the javasript fbu can be helpful when third-party cookies disabled. - fb_js_settings('fbu', 0); - - // Might as well try to clean up the mess. - if (isset($_COOKIE['fbs_' . $fb->getAppId()])) { - setcookie('fbs_' . $fb->getAppId(), '', REQUEST_TIME - 42000, '/'); - } - - } - return $success; -} - - -/** - * Helper to ensure local user is logged out, or an anonymous session is refreshed. - */ -function _fb_logout() { - session_destroy(); - $GLOBALS['user'] = drupal_anonymous_user(); - drupal_session_initialize(); - - // Unsetting the javasript fbu can be helpful when third-party cookies disabled. - fb_js_settings('fbu', 0); - - // Clean up facebook cookies. - if (isset($GLOBALS['_fb_app'])) { - if (isset($_COOKIE['fbs_' . $GLOBALS['_fb_app']->apikey])) { - setcookie('fbs_' . $GLOBALS['_fb_app']->apikey, '', time() - 42000, '/'); - } - if (isset($_COOKIE['fbs_' . $GLOBALS['_fb_app']->id])) { - setcookie('fbs_' . $GLOBALS['_fb_app']->id, '', time() - 42000, '/'); - } - } -} - - -/** - * Returns the facebook user id currently visiting a canvas page, or if - * set_user has been called. Unlike fb_get_fbu(), works only on canvas and - * connect pages, or when infinite session has been initialized. - */ -function fb_facebook_user($fb = NULL) { - if (!isset($fb)) - $fb = $GLOBALS['_fb']; - - if (!$fb) - return; - - try { - $fbu = $fb->getUser(); - return $fbu; - } - catch (FacebookApiException $e) { - fb_log_exception($e, - t('Failed to get Facebook user id. detail: !detail', - array('!detail' => print_r($e, 1)))); - } -} - -/** - * Helper function to ensure user has authorized an application. - * - * Similar to the old require_login() provided by the old facebook API. - * Works by redirecting the user as described in http://developers.facebook.com/docs/authentication/. - * - * @TODO perms if possible, handle users who skip. - */ -function fb_require_authorization($fb = NULL, $destination = NULL) { - if (!$fb) - $fb = $GLOBALS['_fb']; - - if (!$fb) { - throw new Exception(t('Failed to authorize facebook application. Could not determine application id.')); - } - - $fbu = fb_facebook_user($fb); - if (!$fbu) { - $client_id = $fb->getAppId(); - $redirect_uri = $destination ? $destination : url(current_path(), array('absolute' => TRUE, 'fb_canvas' => fb_is_canvas())); - - $url = "https://graph.facebook.com/oauth/authorize?client_id=$client_id&redirect_uri=$redirect_uri"; - if (fb_is_canvas() && module_exists('fb_canvas')) { - fb_canvas_redirect($url); - } - else { - header('Location: ' . $url); // drupal_goto is for internal redirects only. - } - } - else { - return $fbu; - } -} - -/** - * Helper tells other modules when to load admin hooks. - */ -function fb_is_fb_admin_page() { - if (arg(0) == 'admin' && (arg(1) == 'fb' || arg(2) == 'fb')) { - // Keep consistant titles across tabs served by multiple modules. - if ($label = arg(FB_PATH_ADMIN_APPS_ARGS)) - drupal_set_title($label); - else - drupal_set_title(t('Drupal for Facebook')); - - return TRUE; - } -} - -/** - * Given a facebook user id, learn the local uid, if any. - * - */ -function fb_get_uid($fbu, $fb_app = NULL) { - $uid = NULL; - if ($fbu) { - $uid = fb_invoke(FB_OP_GET_UID, array('fbu' => $fbu, 'fb_app' => $fb_app)); - } - return $uid; -} - - -/** - * Given a local user id, find the facebook id. - * - * Invokes hook_fb(FB_OP_GET_FBU) in order to ask other modules what the fbu - * is. Typically, fb_user.module will answer the question. - */ -function fb_get_fbu($uid, $fb_app = NULL) { - // Accept either a user object or uid passed in. - if (is_object($uid) && ($uid->uid) && - isset($uid->fbu) && $uid->fbu) - return $uid->fbu; - elseif (is_object($uid)) - $uid = $uid->uid; - - if ($uid) { - // User management is handled by another module. Use our hook to ask for mapping. - $fbu = fb_invoke(FB_OP_GET_FBU, array('uid' => $uid, - 'fb' => $GLOBALS['_fb'])); - } - else { - $fbu = NULL; - } - return $fbu; -} - -/** - * Convenience function to learn the fbu associated with a user, node or comment. - * Used in theming (X)FBML tags. - */ -function fb_get_object_fbu($object) { - $cache = &drupal_static(__FUNCTION__); - if (!isset($cache)) - $cache = array(); - - if (isset($object->uid) && isset($cache[$object->uid])) { - $fbu = $cache[$object->uid]; - return $fbu; - } - elseif (isset($object->fbu)) { - // Explicitly set. - $fbu = $object->fbu; - } - elseif (isset($object->init) && - ($pos = strpos($object->init, '@facebook'))) { - // Naming convention used by fb_user when creating accounts. - // $object->init may be present when object is a user. - $fbu = substr($object->init, 0, $pos); - } - elseif (isset($object->name) && - $pos = strpos($object->name, '@facebook')) { - $fbu = substr($object->name, 0, $pos); - } - elseif ($object->uid > 0) { - // This can be expensive on pages with many comments or nodes! - $fbu = fb_get_fbu($object->uid); - } - - if (isset($fbu) && is_numeric($fbu)) { - if (isset($object->uid) && ($object->uid > 0)) { - $cache[$object->uid] = $fbu; - } - return $fbu; - } -} - - -/** - * Convenience method to get app info based on apikey or nid. - */ -function fb_get_app($search_data) { - // $search_data can be an apikey, or an array of other search params. - if (!is_array($search_data)) - $search_data = array('apikey' => $search_data); - - $fb_app = fb_invoke(FB_OP_GET_APP, $search_data); - return $fb_app; -} - -/** - * Convenience method for other modules to attach data to the fb_app - * object. - * - * It is assumed the fb_app implementation will fill in the data - * field. We really should clean up the separation between modules, - * or merge fb_app.module into this one. - */ -function fb_get_app_data(&$fb_app) { - if (!isset($fb_app->fb_app_data)) { - $fb_app->fb_app_data = isset($fb_app->data) ? unserialize($fb_app->data) : array(); - } - return $fb_app->fb_app_data; -} - -/** - * Will return a human-readable name if the fb_app module supports it, or - * fb_admin_get_app_properties($fb_app) has been called. However we don't - * take the relatively expensive step of calling that ourselves. - */ -function fb_get_app_title($fb_app) { - if (isset($fb_app->title)) - return $fb_app->title; - elseif (isset($fb_app->application_name)) { - return $fb_app->application_name; - } - else { - return $fb_app->label; - } -} - -/** - * Convenience method to return array of all know fb_apps. - */ -function fb_get_all_apps() { - $apps = fb_invoke(FB_OP_GET_ALL_APPS, NULL, array()); - return $apps; -} - -/** - * A convenience method for returning a list of facebook friends. - * - * This should work efficiently in canvas pages for finding friends of - * the current user. - * - * @TODO - also support users who have permitted offline access. - * - * @return: an array of facebook ids - */ -function fb_get_friends($fbu, $fb_app = NULL) { - $cache = &drupal_static(__FUNCTION__); - if (!$fb_app) - $fb_app = $GLOBALS['_fb_app']; - - // Facebook only allows us to query the current user's friends, so let's try - // to log in as that user. It will only actually work if they are the - // current user of a canvas page, or they've signed up for an infinite - // session. - $fb = fb_api_init($fb_app, $fbu); - if (!$fb || !$fbu) - return; - - $items = array(); - if (!isset($cache[$fbu])) { - if ($fb === $GLOBALS['_fb'] && - $fbu == fb_facebook_user($fb)) { - try { - $items = fb_call_method($fb, 'friends.get', array( - 'uid' => $fbu, - )); - } - catch (Exception $e) { - fb_log_exception($e, t('Failed call to friends.get'), $fb); - } - - } - // friends_get does not work in cron call, so we double check. @TODO - still needed? - if (!$items || !count($items)) { - $logged_in = fb_facebook_user($fb); - $query = "SELECT uid2 FROM friend WHERE uid1=$fbu"; // FQL, no {curly_brackets}! - try { - $result = fb_call_method($fb, 'fql.query', array( - 'query' => $query, - )); - //dpm($result, "FQL " . $query); // debug - } - 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]; -} - -// Return array of facebook gids -function fb_get_groups($fbu, $fb_app = NULL) { - $items = array(); - $groups = fb_get_groups_data($fbu); - - if ($groups && count($groups)) - foreach ($groups as $data) { - $items[] = $data['gid']; - } - return $items; -} - -function fb_get_groups_data($fbu, $fb_app = NULL) { - $cache = &drupal_static(__FUNCTION__); - - $fb = _fb_api_init($fb_app); - if (!$fb || !$fbu) - return; - - if (!isset($cache[$fbu])) { - $cache[$fbu] = fb_call_method($fb, 'groups.get', array( - 'uid' => $fbu, - )); - } - - return $cache[$fbu]; -} - - -//// Menu structure. -/** - * Implements hook_menu(). - */ -function fb_menu() { - $items = array(); - - // Admin pages overview. - $items[FB_PATH_ADMIN] = array( - 'title' => 'Facebook Apps', - 'description' => 'Facebook Applications', - 'page callback' => 'fb_admin_page', - 'access arguments' => array(FB_PERM_ADMINISTER), - 'file' => 'fb.admin.inc', - 'type' => MENU_NORMAL_ITEM, - ); - $items[FB_PATH_ADMIN . '/list'] = array( - 'title' => 'List Apps', - 'weight' => -2, - 'type' => MENU_DEFAULT_LOCAL_TASK, - ); - - $items[FB_PATH_ADMIN . '/settings'] = array( - 'title' => 'Settings', - 'access arguments' => array(FB_PERM_ADMINISTER), - 'weight' => -1, - 'type' => MENU_LOCAL_TASK, - 'page callback' => 'drupal_get_form', - 'page arguments' => array('fb_admin_settings'), - 'file' => 'fb.admin.inc', - ); - - - // Admin pages for each app. - $items[FB_PATH_ADMIN_APPS . '/%fb'] = array( - 'title' => 'Application Detail', - 'description' => 'Facebook Applications', - 'page callback' => 'fb_admin_app_page', - 'page arguments' => array(FB_PATH_ADMIN_APPS_ARGS), - 'access arguments' => array(FB_PERM_ADMINISTER), - 'file' => 'fb.admin.inc', - 'type' => MENU_CALLBACK, - ); - - $items[FB_PATH_ADMIN_APPS . '/%fb/fb'] = array( - 'title' => 'View', - 'weight' => -2, - 'type' => MENU_DEFAULT_LOCAL_TASK, - ); - $items[FB_PATH_ADMIN_APPS . '/%fb/fb/set_props'] = array( - 'title' => 'Set Properties', - 'description' => 'Set Facebook Application Properties', - 'page callback' => 'drupal_get_form', - 'page arguments' => array('fb_admin_set_properties_form', FB_PATH_ADMIN_APPS_ARGS), - 'access arguments' => array(FB_PERM_ADMINISTER), - 'type' => MENU_CALLBACK, - ); - - // Javascript helper - $items['fb/js'] = array( - 'page callback' => 'fb_js_cb', - 'type' => MENU_CALLBACK, - 'access callback' => TRUE, - ); - - // Ajax event handler. - $items[FB_PATH_AJAX_EVENT . '/%'] = array( - 'page callback' => 'fb_ajax_event', - 'type' => MENU_CALLBACK, - 'access callback' => TRUE, - 'page arguments' => array(FB_PATH_AJAX_EVENT_ARGS), - ); - - // "Channel" http://developers.facebook.com/docs/reference/javascript/FB.init - $items['fb/channel'] = array( - 'page callback' => 'fb_channel_page', - 'type' => MENU_CALLBACK, - 'access callback' => TRUE, - ); - - return $items; -} - -/** - * Implementation of a %wildcard_load(). http://drupal.org/node/224170 - * - * Seems to get called a lot(!) so we cache. - */ -function fb_load($id) { - $cache = &drupal_static(__FUNCTION__); - if (!isset($cache)) - $cache = array(); - if (!isset($cache[$id])) { - $query = array('label' => $id); - if (fb_is_fb_admin_page()) { - // Show disabled apps to admins. - $query['status'] = 0; // status >= 0 - } - $cache[$id] = fb_get_app($query); - } - return $cache[$id]; -} - -/** - * Implements hook_permission(). - */ -function fb_permission() { - return array( - FB_PERM_ADMINISTER => array( - 'title' => t('Administer Facebook settings in fb.module'), - ), - ); -} - - -/** - * Implements hook_exit(). - * - * When completing a canvas page we need special processing for the session. See fb_session.inc. - * - * Also invoke hook_fb(FB_OP_EXIT), so that other modules can handle special - * cases (in particular form support in b_canvas.module. - */ -function fb_exit($destination = NULL) { - global $_fb_app, $_fb; - - if ($_fb_app && $_fb) { - - // Invoke other modules. - fb_invoke(FB_OP_EXIT, array('fb_app' => $_fb_app, - 'fb' => $GLOBALS['_fb']), - $destination); - } -} - - -/** - * Invoke hook_fb. - */ -function fb_invoke($op, $data = NULL, $return = NULL, $hook = FB_HOOK) { - foreach (module_implements($hook) as $name) { - $function = $name . '_' . $hook; - try { - $function($op, $data, $return); - } - catch (Exception $e) { - if (isset($data['fb_app'])) { - fb_log_exception($e, t('Exception calling %function(%op) (!app)', array( - '%function' => $function, - '%op' => $op, - '%label' => $data['fb_app']->label, - '%apikey' => $data['fb_app']->apikey, - '!app' => l($data['fb_app']->label, FB_PATH_ADMIN_APPS . '/' . $data['fb_app']->label), - ))); - } - else { - fb_log_exception($e, t('Exception calling %function(%op)', array( - '%function' => $function, - '%op' => $op))); - } - } - } - return $return; -} - -/** - * This method will clean up URLs. When serving canvas pages, extra - * information is included in URLs. This will remove the extra - * information. Useful when linking back to the website from a canvas page or - * wall post. - * - * For example in the following code, $url2 will link out to the server's domain: - * - * $url = url('node/42', array('absolute' => TRUE)); // i.e. http://apps.facebook.com/example/node/42 - * $url2 = fb_scrub_urls($url); // i.e. http://example.com/node/42 - * - * - * @see fb_url_rewrite.inc - */ -function fb_scrub_urls($content) { - if (function_exists('_fb_settings_url_rewrite_prefixes')) { - foreach (_fb_settings_url_rewrite_prefixes() as $key) { - $patterns[] = "|$key/[^/]*/|"; - $replacements[] = ""; - } - $content = preg_replace($patterns, $replacements, $content); - } - return $content; -} - -/** - * Convenience function to log and report exceptions. - */ -function fb_log_exception($e, $text = '', $fb = NULL) { - if ($text) - $message = $text . ': ' . $e->getMessage(); - else - $message = $e->getMessage(); - $message .= ' ' . $e->getCode(); - - if ($fb) { - $message .= '. (' . t('logged into facebook as %fbu', array('%fbu' => $fb->getUser())) . ')'; - } - if (fb_verbose()) { - $message .= '
' . $e . '
'; - } - watchdog('fb', $message, array(), WATCHDOG_ERROR); - if (user_access(FB_PERM_ADMINISTER)) { - drupal_set_message($message, 'error'); - } -} - -/** - * Exception handler for PHP5 exceptions. - */ - function fb_handle_exception($exception) { - $message = t('Facebook API exception %message. !trace', array( - '%message' => $exception->getMessage(), - '!trace' => '
' . $exception->getTraceAsString() . '
', - )); - watchdog('fb', $message, array(), WATCHDOG_ERROR); - //drupal_set_message($message, 'error'); - print $message; - - print "
\$_REQUEST:\n";
-  print_r($_REQUEST);
-  print "\n\nREQUEST_URI:\n" . request_uri();
-  print "
"; - -} - -/** - * Simple wrapper around $fb->api() which caches data. Does not support the - * polymorphic arguments of $fb->api(). This is not a replacement for that - * function. - * - * This is intended to avoid performace problems when, for example, - * $fb->api('me') is called several times in a single request. - * - * @param $graph_path - * Something facebook's graph API will understand. Could be an ID or a path like 'me/accounts', for example. - * - * @param $params - * Extras to pass to the graph API. When making a request that requires a - * token, try array('access_token' => fb_get_token()). - */ -function fb_api($graph_path, $params = array()) { - static $cache; - $fb = $GLOBALS['_fb']; - if (!$fb) { - return; - } - if (!isset($cache)) { - $cache = array(); - } - if (!isset($cache[$graph_path])) { - $cache[$graph_path] = $fb->api($graph_path, $params); - } - return $cache[$graph_path]; -} - -/** - * DEPRECATED. Use fb_api() instead. - * Returns information about one or more facebook users. - * - * Historically, this helper function used facebook's users_getInfo API, hence - * the name. Now it uses fql.query, but accomplishes the same thing. - * - * @param $oids - * Array of facebook object IDs. In this case they should each be a user id. - * - * @param $fb - * Rarely needed. For cases when global $_fb is not set, or more than one - * facebook api has been initialized. - * - * @param $refresh_cache - * If true, force a call to facebook instead of relying on temporarily stored - * data. - */ -function fb_users_getInfo($oids, $fb = NULL, $refresh_cache = FALSE) { - if (!$fb) { - $fb = $GLOBALS['_fb']; - } - $infos = array(); - - if (!is_array($oids)) - $oids = array(); - - if ($fb) { - $app_id = $fb->getAppId(); - // First try cache - if (!$refresh_cache && isset($_SESSION['fb'])) { - foreach ($oids as $oid) { - if (isset($_SESSION['fb'][$app_id]['userinfo'][$oid])) { - $info = $_SESSION['fb'][$app_id]['userinfo'][$oid]; - $infos[] = $info; - } - } - } - if (count($infos) != count($oids)) { - // Session cache did not include all users, update the cache. - $fields = array( - 'about_me', - 'affiliations', - 'name', - 'is_app_user', - 'pic', - 'pic_big', - 'pic_square', - 'profile_update_time', - 'proxied_email', - 'status', - 'email_hashes', - 'email', - 'uid', - ); - try { - $infos = fb_fql_query($fb, 'SELECT ' . implode(', ', $fields) . ' FROM user WHERE uid in(' . implode(', ', $oids) . ')', array(fb_get_token($fb))); - // Update cache with recent results. - if (is_array($infos)) { - foreach ($infos as $info) { - $_SESSION['fb'][$app_id]['userinfo'][$info['uid']] = $info; - } - } - } catch (FacebookApiException $e) { - fb_log_exception($e, t('Failed to query facebook user info'), $fb); - } - } - - return $infos; - } -} - -/** - * For debugging, add $conf['fb_verbose'] = TRUE; to settings.php. - */ -function fb_verbose() { - return variable_get(FB_VAR_VERBOSE, NULL); -} - -/** - * This function will be replaced, hopefully, by format_username in D7. - * - * @see http://drupal.org/node/192056 - */ -function fb_format_username($account) { - $name = !empty($account->name) ? $account->name : variable_get('anonymous', t('Anonymous')); - drupal_alter('username', $name, $account); - return $name; -} - -/** - * hook_username_alter(). - * - * Return a user's facebook name, instead of local username. - * @TODO cache, because drupal doesn't, and calls this often. - */ -function fb_username_alter(&$name, $account) { - // This function can be called very early in the bootstrap process, before - // the modules are initialized, in which case we will fail to alter the - // name. - if ($GLOBALS['_fb'] && $fbu = fb_get_fbu($account)) { // @TODO - is fb_get_fbu() a performance hit here? - try { - // Use fql query instead of graph api, because it will succeed more often. - $data = fb_fql_query($GLOBALS['_fb'], "SELECT name FROM user WHERE uid=$fbu", array( - 'access_token' => fb_get_token($GLOBALS['_fb']), - )); - if (count($data) && isset($data[0]['name'])) { - $name = $data[0]['name']; - } - } - catch (Exception $e) { - fb_log_exception($e, t('Failed to alter username for facebook user %fbu', array( - '%fbu' => $fbu))); - } - } -} - -/** - * Implements hook_theme(). - * - * Returns description of theme functions. - * - * @see fb.theme.inc - */ -function fb_theme() { - return array( - 'fb_username' => array( - 'arguments' => array( - 'fbu' => NULL, - 'object' => NULL, - 'orig' => NULL, - ), - 'file' => 'fb.theme.inc', - ), - 'fb_user_picture' => array( - 'arguments' => array( - 'fbu' => NULL, - 'account' => NULL, - 'orig' => NULL, - ), - 'file' => 'fb.theme.inc', - ), - 'fb_fbml_popup' => array( - 'arguments' => array('elements' => NULL), - 'file' => 'fb.theme.inc', - ), - 'fb_login_button' => array( - 'arguments' => array( - 'text' => 'Connect with Facebook', - 'options' => NULL), - 'file' => 'fb.theme.inc', - ), - ); -} - -//// Javascript and Ajax helpers - -/** - * Ajax javascript callback. - * - * For sites which use ajax, various events may create javascript which is - * normally embedded in a page. For example, posting to a user's wall. When - * ajax is used instead of a page reload, this callback will provide any - * javascript which should be run. - */ -function fb_js_cb() { - $js_array = fb_invoke(FB_OP_JS, array('fb' => $GLOBALS['_fb'], 'fb_app' => $GLOBALS['_fb_app']), array()); - $extra_js = implode("\n", $extra); - print $extra_js; - exit(); -} - -/** - * Ajax callback handles an event from facebook's javascript sdk. - * - * @see - * fb.js and - * http://developers.facebook.com/docs/reference/javascript/FB.Event.subscribe - * - * @return - * Array of javascript to be evaluated by the page which called this - * callback. - */ -function fb_ajax_event($event_type) { - global $_fb, $_fb_app; - $js_array = array(); - - if (isset($_REQUEST['apikey'])) { - $_fb_app = fb_get_app(array('apikey' => $_REQUEST['apikey'])); - if ($_fb_app) { - $_fb = fb_api_init($_fb_app); - // Data to pass to hook_fb. - $data = array( - 'fb_app' => $_fb_app, - 'fb' => $_fb, - 'event_type' => $event_type, - 'event_data' => $_POST, // POSTed via ajax. - ); - - $js_array = fb_invoke(FB_OP_AJAX_EVENT, $data, array()); - - } - else { - watchdog('fb', 'fb_ajax_event did not find application %id', array('%id' => $_REQUEST['apikey']), WATCHDOG_ERROR); - } - - if ($event_type == 'session_change') { - // Session change is a special case. If user has logged out of - // facebook, we want a new drupal session. We do this here, even if - // fb_user.module is not enabled. - if (!isset($_POST['fbu']) || !$_POST['fbu']) { // Logout, not login. - _fb_logout(); - } - } - - } - else { - watchdog('fb', 'fb_ajax_event called badly. Not passed apikey.', array(), WATCHDOG_ERROR); - // Trying to track down what makes this happen. - if (fb_verbose() == 'extreme') { - watchdog('fb', 'fb_ajax_event called badly. Not passed apikey. trace: !trace', array( - '!trace' => '
' . print_r(debug_backtrace(), 1) . '
', - ), WATCHDOG_ERROR); - } - } - drupal_json_output($js_array); - exit(); -} - -/** - * Menu callback for custom channel. - * - * @see http://developers.facebook.com/docs/reference/javascript/FB.init - */ -function fb_channel_page() { - //headers instructing browser to cache this page. - // Do these work? - drupal_add_http_header("Cache-Control", "public"); - drupal_add_http_header("Expires", "Sun, 17-Jan-2038 19:14:07 GMT"); - - $date = format_date(time()); - $output = "\n"; - $url = fb_js_settings('js_sdk_url'); - $output .= "\n"; - print $output; - exit(); -} - -//// Miscellaneous helpers and convenience functions. - -/** - * Protocol (http or https) of the current request. - */ -function fb_protocol() { - return (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on') ? 'https' : 'http'; -} - - -/** - * Convenience wrapper around drupal_access_denied(). Call on pages where the - * access is denied because the user is not logged into facebook. - */ -function fb_access_denied() { - if (!fb_facebook_user()) { - drupal_set_message(t('You must log into facebook to view this page.')); - } - drupal_access_denied(); - exit(); -} - + TRUE, + ))); + + // Data structure to pass to FB.init(); + $fb_init_settings = array( + 'xfbml' => FALSE, + 'status' => FALSE, + 'cookie' => variable_get(FB_VAR_USE_COOKIE, TRUE), + ); + + if ($_fb_app) { + // An App is configured. + + // Javascript settings needed by fb.js. + fb_js_settings('apikey', $_fb_app->apikey); + fb_js_settings('label', $_fb_app->label); + fb_js_settings('page_type', fb_settings(FB_SETTINGS_TYPE)); // canvas or connect. + $fb_init_settings['apiKey'] = $_fb_app->apikey; + $fb_init_settings['appId'] = $_fb_app->id; + + // Initialize the PHP API. + $_fb = fb_api_init($_fb_app); + + if ($_fb) { + + // Look for session info from several sources. + if ($session = $_fb->getUser()) { + // 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. + $session = json_decode($_REQUEST['fb_js_session'], TRUE); + } + elseif (isset($_SESSION['fb'][$_fb_app->apikey]['session']) && arg(0) != 'logout') { + // Use the session previously stored. + $session = $_SESSION['fb'][$_fb_app->apikey]['session']; + } + + // Set access token for SDK version 3.0 + $_fb->setAccessToken($session['access_token']); + + // 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->apikey]['session'] = $session; + } + + // Make javascript work even when third-party cookies disabled. + $fb_init_settings['session'] = $session; + + // 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->apikey]); + unset($fb_init_settings['session']); + } + } + + // Give other modules a chance to initialize. + fb_invoke(FB_OP_INITIALIZE, array( + 'fb_app' => $_fb_app, + 'fb' => $_fb, + )); + + // See if the facebook user id is known + if ($fbs = $session) { + fb_invoke(FB_OP_APP_IS_AUTHORIZED, array( + 'fb_app' => $_fb_app, + 'fb' => $_fb, + 'fbu' => $_fb->getUser(), + )); + fb_js_settings('fbu', $_fb->getUser()); + } + else { + // Add perms to settings, for calling FB.login(). + $perms = array(); + drupal_alter('fb_required_perms', $perms); + fb_js_settings('perms', implode(',', $perms)); + } + } + else + watchdog('fb', "URL indicates a facebook app, but could not initialize Facebook", array(), WATCHDOG_ERROR); + + if (isset($_REQUEST['destination'])) { + $destination = $_REQUEST['destination']; + } + elseif (current_path()) { + $destination = current_path(); + } + else { + $destination = ''; + } + if (fb_is_canvas()) { + $destination = fb_scrub_urls($destination); // Needed? + } + + //Stripping the fragment out to be tacked on during the javascript redirect + if (strpos($destination, '#') !== FALSE) { + list($destination, $fragment) = explode('#', $destination, 2); + fb_js_settings('reload_url_fragment', $fragment); + } + + fb_js_settings('reload_url', url($destination, array( + 'absolute' => TRUE, + 'fb_canvas' => fb_is_canvas(), + 'language' => (object) array('prefix' => NULL, 'language' => NULL), // http://drupal.org/node/1000452 + ))); + + } + + if ($channel = variable_get(FB_VAR_JS_CHANNEL, TRUE)) { + if (!is_string($channel)) { + $channel = url('fb/channel', array('absolute' => TRUE, 'fb_url_alter' => FALSE)); + } + $fb_init_settings['channelUrl'] = $channel; + } + + fb_js_settings('fb_init_settings', $fb_init_settings); + + + // Allow third-parties to act, even if we did not initialize $_fb. + fb_invoke(FB_OP_POST_INIT, array('fb_app' => $_fb_app, + 'fb' => $_fb)); + + fb_js_settings('controls', implode(',', fb_controls())); + + if (!fb_js_settings('js_sdk_url')) { + if (isset($_SESSION['fb_locale']) && + variable_get(FB_VAR_LANGUAGE_OVERRIDE, 'override')) { + // @TODO - get locale from signed request. It appears to contain it now. + $fb_lang = $_SESSION['fb_locale']; + } + else { + $user_language = user_preferred_language($GLOBALS['user']); + $fb_lang = variable_get('fb_language_' . $user_language->language, 'en_US'); + } + + $js_sdk = fb_protocol() . "://connect.facebook.net/$fb_lang/all.js"; + fb_js_settings('js_sdk_url', variable_get(FB_VAR_JS_SDK, $js_sdk)); + } + + // Add our module's javascript. + drupal_add_js(drupal_get_path('module', 'fb') . '/fb.js', array( + 'type' => 'file', + 'scope' => 'header', + 'group' => JS_LIBRARY, + )); + + // See also fb_page_alter(), where we initialize facebook's SDK. +} + +/** + * Implements hook_rdf_namespaces(). + * Adds the xmlns:fb="http://www.facebook.com/2008/fbml" attribute to html tag. + */ +function fb_rdf_namespaces() { + return array( + 'fb' => 'http://www.facebook.com/2008/fbml', + ); +} + +/** + * Helper to get the configured variables. + * + * Adds the javascript setting with the supplied key/value. This function + * merely keeps track of the settings and writes them as late as possible. + * Currently, in the fb_footer() function. There has been a lot of + * experimentation as to the best place to initialize the facebook javascript + * SDK. The footer appears to be the best place because we may not know all + * settings until well after hook_init(). + * + * @param $key + * The javascript setting name. If the key is null then nothing is modified and the settings are returned. + * @param $value + * The value of the javascript setting. If the key is not NULL by the value is the setting is removed + * @return + * The associative array containing the current fb javascript settings + */ +function fb_js_settings($key = NULL, $value = NULL) { + $fb_js_settings = &drupal_static(__FUNCTION__); + + if (isset($key) && isset($value)) { + $fb_js_settings[$key] = $value; + return $value; + } + elseif (isset($key)) { + return isset($fb_js_settings[$key]) ? $fb_js_settings[$key] : NULL; + } + else { + return $fb_js_settings; + } +} + + +/** + * Include and initialize Facebook's PHP SDK. + */ +function fb_api_init($fb_app) { + $cache = &drupal_static(__FUNCTION__); + // This helps with uncaught exceptions. However, it should be configurable + // or at least not overwrite previously declared handler. + set_exception_handler('fb_handle_exception'); + + if (isset($cache[$fb_app->apikey])) { + return $cache[$fb_app->apikey]; + } + + // Find Facebook's PHP SDK. Use libraries API if enabled. + $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'); + + try { + if (!class_exists('Facebook') && !include($fb_platform)) { + $message = t('Failed to find the Facebook client libraries at %filename. Read the !readme and follow the instructions carefully.', array( + '!drupal_for_facebook' => l(t('Drupal for Facebook'), 'http://drupal.org/project/fb'), + // This link should work with clean URLs disabled. + '!readme' => 'README.txt', + '%filename' => $filename, + )); + drupal_set_message($message, 'error'); + watchdog('fb', $message); + return NULL; + } + + // We don't have a cached resource for this app, so we're going to create one. + $fb = new Facebook(array( + 'appId' => $fb_app->id, + 'secret' => isset($fb_app->secret) ? $fb_app->secret : NULL, + 'cookie' => variable_get(FB_VAR_USE_COOKIE, TRUE), + )); + + // Some servers need these settings. + if (variable_get(FB_VAR_CURL_NOVERIFY, TRUE)) { + Facebook::$CURL_OPTS[CURLOPT_SSL_VERIFYPEER] = FALSE; + Facebook::$CURL_OPTS[CURLOPT_SSL_VERIFYHOST] = FALSE; + //Facebook::$CURL_OPTS[CURLOPT_VERBOSE] = 1; // debug + } + + // Cache the result, in case we're called again. + $cache[$fb_app->apikey] = $fb; + + return $fb; + } + catch (Exception $e) { + fb_log_exception($e, t('Failed to construct Facebook client API.')); + } +} + +/** + * Wrapper function for fb_api_init. This helps for functions that should + * work whether or not we are on a canvas page. For canvas pages, the active + * fb object is used. For non-canvas pages, it will initialize the API using + * an infinite session, if configured. + * + * @param $fb_app Note this is ignored on canvas pages. + * + * This is for internal use. Third party modules use fb_api_init(). + */ +function _fb_api_init($fb_app = NULL) { + $fb = $GLOBALS['_fb']; // Default to active app on canvas pages + if (!$fb && $fb_app) + // Otherwise, log into facebook api. + $fb = fb_api_init($fb_app, FB_FBU_ANY); + + if (!$fb) { + watchdog('fb', '%function unable to initialize Facebook API.', + array('%function' => '_fb_api_init()'), WATCHDOG_ERROR); + return; + } + else + return $fb; +} + + +/** + * Helper function to get the most commonly used values. In your custom + * module, call extract(fb_vars()); to set $fb_app, $fb, and $fbu. + */ +function fb_vars() { + return array( + 'fb' => $GLOBALS['_fb'], + 'fb_app' => $GLOBALS['_fb_app'], + 'fbu' => fb_facebook_user(), + ); +} + + +/** + * Helper to get the tokens needed to accss facebook's API. + * + * You would think that facebook's SDK would provide basic functions like this. + * + * @param $fb + * Get the token for this API instance. If NULL, use the global $_fb. + * + * @param $fbu + * Get the user-specific token. If NULL, get the application token. + */ +function fb_get_token($fb = NULL, $fbu = NULL) { + $cache = &drupal_static(__FUNCTION__); + if (!isset($cache)) + $cache = array(); + + if (!$fb) { + $fb = $GLOBALS['_fb']; + } + if (!$fb) { + return; + } + + $app_id = $fb->getAppId(); + $cache_key = $app_id; + + // @TODO if both fb and fbu are NULL, it might be better performance to use the current user's token, if available. + + if (!$fbu) { + // Get the application token. + if (!isset($cache[$cache_key])) { + $path = "https://graph.facebook.com/oauth/access_token?client_id=" . $app_id . "&client_secret=" . $fb->getApiSecret() . "&type=client_cred"; + $http = drupal_http_request($path); + if ($http->code == 200 && isset($http->data)) { + $data = explode('=', $http->data); + $token = $data[1]; + if ($token) + $cache[$cache_key] = $token; + } + } + } + else { + $cache_key .= '_' . $fbu; + // Get the user access token. + if ($fbu == 'me' || $fbu == fb_facebook_user($fb)) { + $cache[$cache_key] = getAccessToken(); + } + else { + $session_data = fb_invoke(FB_OP_GET_USER_SESSION, array( + 'fb' => $fb, + 'fb_app' => fb_get_app(array('id' => $app_id)), + 'fbu' => $fbu, + ), array()); + if (count($session_data)) { + $cache[$cache_key] = $session_data['access_token']; + } + } + } + return isset($cache[$cache_key]) ? $cache[$cache_key] : NULL; +} + +/** + * This helper original written because facebook's $fb->api() function was + * very buggy. I'm not sure this is still needed. On the other hand, a + * future version of modules/fb might use this instead of faceobok's PHP SDK, + * eliminating the need for it entirely. + */ +function fb_call_method($fb, $method, $params = array()) { + $params['access_token'] = fb_get_token($fb); + $params['format'] = 'json-strings'; + + // Here's how to create a url that conforms to standards: + $url = url("https://api.facebook.com/method/{$method}", array( + 'query' => $params, + )); + // If facebook gives errors like "Invalid OAuth 2.0 Access Token 190/Unable to get application prop" it might be necessary to uncomment the urldecode below. + // http://forum.developers.facebook.net/viewtopic.php?id=76228 + // $url = rawurldecode($url); + + $http = drupal_http_request($url); + + if (!isset($http->error) && isset($http->data)) { + $data = json_decode($http->data, TRUE); + // Yes, it's double encoded. At least sometimes. + if (is_string($data)) { + $data = json_decode($data, TRUE); + } + if (is_array($data)) { + if (isset($data['error_code'])) { + throw new FacebookApiException($data); + } + } + elseif ($http->data == 'true' || $http->code == 200) { + // No problems. + } + else { + // Never reach this??? + if (function_exists('dpm')) dpm($http, __FUNCTION__ . " unexpected result from $url"); // XXX + } + return $data; + } + else { + // Should we throw FacebookApiException, or plain old exception? + throw new FacebookApiException( + array( + 'error_msg' => t('fb_call_method failed calling !method. !detail', array( + '!method' => $method, + '!detail' => $http->error, + )), + 'error_code' => $http->code, + )); + } +} + +/** + * Helper function for fql queries. + * + * Use $params to pass a session_key, when needed. + */ +function fb_fql_query($fb, $query, $params = array()) { + $params['query'] = $query; + //$result = fb_call_method($fb, 'fql.query', $params); + $params['method'] = 'fql.query'; + $result = $fb->api($params); + + return $result; +} + + +/** + * Implements hook_page_alter(). + * Can alter the $page['page_bottom'] hidden region here. + */ +function fb_page_alter(&$page) { + global $_fb, $_fb_app; + + // This element recommended by facebook. http://developers.facebook.com/docs/reference/javascript/ + $output = "
\n"; + + $settings = fb_js_settings(); + + $output .= "\n"; + + $page['page_bottom']['fb'] = array( + '#type' => 'markup', + '#markup' => $output, + ); +} + +/** + * Is the current request a canvas page? + */ +function fb_is_canvas() { + if (fb_is_tab()) { + return FALSE; + } + elseif (fb_settings(FB_SETTINGS_CB)) { + // Using fb_url_rewrite. + return TRUE; + } + elseif (fb_settings(FB_SETTINGS_TYPE) == FB_SETTINGS_TYPE_CANVAS) { + // No rewrite, but fb_settings.inc has detected type. + return TRUE; + } + return FALSE; +} + +/** + * Is the current page a profile tab. + * + * Only works when "Canvas Session Parameter" is disabled. + */ +function fb_is_tab() { + global $_fb; + + if (fb_settings(FB_SETTINGS_TYPE) == FB_SETTINGS_TYPE_PAGE_TAB) { + return TRUE; + } + elseif (fb_settings(FB_SETTINGS_TYPE) == FB_SETTINGS_TYPE_PROFILE) { // deprecated FBML tab + return TRUE; + } + elseif (isset($_REQUEST['fb_sig_in_profile_tab']) && + $_REQUEST['fb_sig_in_profile_tab']) { // deprecated ancient history + // Old way, no migrations enabled. + return TRUE; + } + return FALSE; +} + + +/** + * Sometimes calls to fb_api_init succeed, but calls to the client api + * will fail because cookies are obsolete or what have you. This + * function makes a call to facebook to test the session. Expensive, + * so use only when necessary. + * + */ +function fb_api_check_session($fb) { + $success = FALSE; + try { + $me = $fb->api('me'); + + // Store the locale if set. + if (isset($me['locale'])) { + $_SESSION['fb_locale'] = $me['locale']; + } + + // Does not matter what is returned, as long as exception is not thrown. + $success = TRUE; + } + catch (Exception $e) { + if (fb_verbose()) { + watchdog('fb', 'fb_api_check_session failed. Possible attempt to spoof a facebook session!'); + } + $success = FALSE; + if (fb_verbose()) { + fb_log_exception($e, t("fb_api_check_session failed.")); + } + + unset($_SESSION['fb'][$fb->getAppId()]); + // Unsetting the javasript fbu can be helpful when third-party cookies disabled. + fb_js_settings('fbu', 0); + + // Might as well try to clean up the mess. + if (isset($_COOKIE['fbs_' . $fb->getAppId()])) { + setcookie('fbs_' . $fb->getAppId(), '', REQUEST_TIME - 42000, '/'); + } + + } + return $success; +} + + +/** + * Helper to ensure local user is logged out, or an anonymous session is refreshed. + */ +function _fb_logout() { + session_destroy(); + $GLOBALS['user'] = drupal_anonymous_user(); + drupal_session_initialize(); + + // Unsetting the javasript fbu can be helpful when third-party cookies disabled. + fb_js_settings('fbu', 0); + + // Clean up facebook cookies. + if (isset($GLOBALS['_fb_app'])) { + if (isset($_COOKIE['fbs_' . $GLOBALS['_fb_app']->apikey])) { + setcookie('fbs_' . $GLOBALS['_fb_app']->apikey, '', time() - 42000, '/'); + } + if (isset($_COOKIE['fbs_' . $GLOBALS['_fb_app']->id])) { + setcookie('fbs_' . $GLOBALS['_fb_app']->id, '', time() - 42000, '/'); + } + } +} + + +/** + * Returns the facebook user id currently visiting a canvas page, or if + * set_user has been called. Unlike fb_get_fbu(), works only on canvas and + * connect pages, or when infinite session has been initialized. + */ +function fb_facebook_user($fb = NULL) { + if (!isset($fb)) + $fb = $GLOBALS['_fb']; + + if (!$fb) + return; + + try { + $fbu = $fb->getUser(); + return $fbu; + } + catch (FacebookApiException $e) { + fb_log_exception($e, + t('Failed to get Facebook user id. detail: !detail', + array('!detail' => print_r($e, 1)))); + } +} + +/** + * Helper function to ensure user has authorized an application. + * + * Similar to the old require_login() provided by the old facebook API. + * Works by redirecting the user as described in http://developers.facebook.com/docs/authentication/. + * + * @TODO perms if possible, handle users who skip. + */ +function fb_require_authorization($fb = NULL, $destination = NULL) { + if (!$fb) + $fb = $GLOBALS['_fb']; + + if (!$fb) { + throw new Exception(t('Failed to authorize facebook application. Could not determine application id.')); + } + + $fbu = fb_facebook_user($fb); + if (!$fbu) { + $client_id = $fb->getAppId(); + $redirect_uri = $destination ? $destination : url(current_path(), array('absolute' => TRUE, 'fb_canvas' => fb_is_canvas())); + + $url = "https://graph.facebook.com/oauth/authorize?client_id=$client_id&redirect_uri=$redirect_uri"; + if (fb_is_canvas() && module_exists('fb_canvas')) { + fb_canvas_redirect($url); + } + else { + header('Location: ' . $url); // drupal_goto is for internal redirects only. + } + } + else { + return $fbu; + } +} + +/** + * Helper tells other modules when to load admin hooks. + */ +function fb_is_fb_admin_page() { + if (arg(0) == 'admin' && (arg(1) == 'fb' || arg(2) == 'fb')) { + // Keep consistant titles across tabs served by multiple modules. + if ($label = arg(FB_PATH_ADMIN_APPS_ARGS)) + drupal_set_title($label); + else + drupal_set_title(t('Drupal for Facebook')); + + return TRUE; + } +} + +/** + * Given a facebook user id, learn the local uid, if any. + * + */ +function fb_get_uid($fbu, $fb_app = NULL) { + $uid = NULL; + if ($fbu) { + $uid = fb_invoke(FB_OP_GET_UID, array('fbu' => $fbu, 'fb_app' => $fb_app)); + } + return $uid; +} + + +/** + * Given a local user id, find the facebook id. + * + * Invokes hook_fb(FB_OP_GET_FBU) in order to ask other modules what the fbu + * is. Typically, fb_user.module will answer the question. + */ +function fb_get_fbu($uid, $fb_app = NULL) { + // Accept either a user object or uid passed in. + if (is_object($uid) && ($uid->uid) && + isset($uid->fbu) && $uid->fbu) + return $uid->fbu; + elseif (is_object($uid)) + $uid = $uid->uid; + + if ($uid) { + // User management is handled by another module. Use our hook to ask for mapping. + $fbu = fb_invoke(FB_OP_GET_FBU, array('uid' => $uid, + 'fb' => $GLOBALS['_fb'])); + } + else { + $fbu = NULL; + } + return $fbu; +} + +/** + * Convenience function to learn the fbu associated with a user, node or comment. + * Used in theming (X)FBML tags. + */ +function fb_get_object_fbu($object) { + $cache = &drupal_static(__FUNCTION__); + if (!isset($cache)) + $cache = array(); + + if (isset($object->uid) && isset($cache[$object->uid])) { + $fbu = $cache[$object->uid]; + return $fbu; + } + elseif (isset($object->fbu)) { + // Explicitly set. + $fbu = $object->fbu; + } + elseif (isset($object->init) && + ($pos = strpos($object->init, '@facebook'))) { + // Naming convention used by fb_user when creating accounts. + // $object->init may be present when object is a user. + $fbu = substr($object->init, 0, $pos); + } + elseif (isset($object->name) && + $pos = strpos($object->name, '@facebook')) { + $fbu = substr($object->name, 0, $pos); + } + elseif ($object->uid > 0) { + // This can be expensive on pages with many comments or nodes! + $fbu = fb_get_fbu($object->uid); + } + + if (isset($fbu) && is_numeric($fbu)) { + if (isset($object->uid) && ($object->uid > 0)) { + $cache[$object->uid] = $fbu; + } + return $fbu; + } +} + + +/** + * Convenience method to get app info based on apikey or nid. + */ +function fb_get_app($search_data) { + // $search_data can be an apikey, or an array of other search params. + if (!is_array($search_data)) + $search_data = array('apikey' => $search_data); + + $fb_app = fb_invoke(FB_OP_GET_APP, $search_data); + return $fb_app; +} + +/** + * Convenience method for other modules to attach data to the fb_app + * object. + * + * It is assumed the fb_app implementation will fill in the data + * field. We really should clean up the separation between modules, + * or merge fb_app.module into this one. + */ +function fb_get_app_data(&$fb_app) { + if (!isset($fb_app->fb_app_data)) { + $fb_app->fb_app_data = isset($fb_app->data) ? unserialize($fb_app->data) : array(); + } + return $fb_app->fb_app_data; +} + +/** + * Will return a human-readable name if the fb_app module supports it, or + * fb_admin_get_app_properties($fb_app) has been called. However we don't + * take the relatively expensive step of calling that ourselves. + */ +function fb_get_app_title($fb_app) { + if (isset($fb_app->title)) + return $fb_app->title; + elseif (isset($fb_app->application_name)) { + return $fb_app->application_name; + } + else { + return $fb_app->label; + } +} + +/** + * Convenience method to return array of all know fb_apps. + */ +function fb_get_all_apps() { + $apps = fb_invoke(FB_OP_GET_ALL_APPS, NULL, array()); + return $apps; +} + +/** + * A convenience method for returning a list of facebook friends. + * + * This should work efficiently in canvas pages for finding friends of + * the current user. + * + * @TODO - also support users who have permitted offline access. + * + * @return: an array of facebook ids + */ +function fb_get_friends($fbu, $fb_app = NULL) { + $cache = &drupal_static(__FUNCTION__); + if (!$fb_app) + $fb_app = $GLOBALS['_fb_app']; + + // Facebook only allows us to query the current user's friends, so let's try + // to log in as that user. It will only actually work if they are the + // current user of a canvas page, or they've signed up for an infinite + // session. + $fb = fb_api_init($fb_app, $fbu); + if (!$fb || !$fbu) + return; + + $items = array(); + if (!isset($cache[$fbu])) { + if ($fb === $GLOBALS['_fb'] && + $fbu == fb_facebook_user($fb)) { + try { + $items = fb_call_method($fb, 'friends.get', array( + 'uid' => $fbu, + )); + } + catch (Exception $e) { + fb_log_exception($e, t('Failed call to friends.get'), $fb); + } + + } + // friends_get does not work in cron call, so we double check. @TODO - still needed? + if (!$items || !count($items)) { + $logged_in = fb_facebook_user($fb); + $query = "SELECT uid2 FROM friend WHERE uid1=$fbu"; // FQL, no {curly_brackets}! + try { + $result = fb_call_method($fb, 'fql.query', array( + 'query' => $query, + )); + //dpm($result, "FQL " . $query); // debug + } + 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]; +} + +// Return array of facebook gids +function fb_get_groups($fbu, $fb_app = NULL) { + $items = array(); + $groups = fb_get_groups_data($fbu); + + if ($groups && count($groups)) + foreach ($groups as $data) { + $items[] = $data['gid']; + } + return $items; +} + +function fb_get_groups_data($fbu, $fb_app = NULL) { + $cache = &drupal_static(__FUNCTION__); + + $fb = _fb_api_init($fb_app); + if (!$fb || !$fbu) + return; + + if (!isset($cache[$fbu])) { + $cache[$fbu] = fb_call_method($fb, 'groups.get', array( + 'uid' => $fbu, + )); + } + + return $cache[$fbu]; +} + + +//// Menu structure. +/** + * Implements hook_menu(). + */ +function fb_menu() { + $items = array(); + + // Admin pages overview. + $items[FB_PATH_ADMIN] = array( + 'title' => 'Facebook Apps', + 'description' => 'Facebook Applications', + 'page callback' => 'fb_admin_page', + 'access arguments' => array(FB_PERM_ADMINISTER), + 'file' => 'fb.admin.inc', + 'type' => MENU_NORMAL_ITEM, + ); + $items[FB_PATH_ADMIN . '/list'] = array( + 'title' => 'List Apps', + 'weight' => -2, + 'type' => MENU_DEFAULT_LOCAL_TASK, + ); + + $items[FB_PATH_ADMIN . '/settings'] = array( + 'title' => 'Settings', + 'access arguments' => array(FB_PERM_ADMINISTER), + 'weight' => -1, + 'type' => MENU_LOCAL_TASK, + 'page callback' => 'drupal_get_form', + 'page arguments' => array('fb_admin_settings'), + 'file' => 'fb.admin.inc', + ); + + + // Admin pages for each app. + $items[FB_PATH_ADMIN_APPS . '/%fb'] = array( + 'title' => 'Application Detail', + 'description' => 'Facebook Applications', + 'page callback' => 'fb_admin_app_page', + 'page arguments' => array(FB_PATH_ADMIN_APPS_ARGS), + 'access arguments' => array(FB_PERM_ADMINISTER), + 'file' => 'fb.admin.inc', + 'type' => MENU_CALLBACK, + ); + + $items[FB_PATH_ADMIN_APPS . '/%fb/fb'] = array( + 'title' => 'View', + 'weight' => -2, + 'type' => MENU_DEFAULT_LOCAL_TASK, + ); + $items[FB_PATH_ADMIN_APPS . '/%fb/fb/set_props'] = array( + 'title' => 'Set Properties', + 'description' => 'Set Facebook Application Properties', + 'page callback' => 'drupal_get_form', + 'page arguments' => array('fb_admin_set_properties_form', FB_PATH_ADMIN_APPS_ARGS), + 'access arguments' => array(FB_PERM_ADMINISTER), + 'type' => MENU_CALLBACK, + ); + + // Javascript helper + $items['fb/js'] = array( + 'page callback' => 'fb_js_cb', + 'type' => MENU_CALLBACK, + 'access callback' => TRUE, + ); + + // Ajax event handler. + $items[FB_PATH_AJAX_EVENT . '/%'] = array( + 'page callback' => 'fb_ajax_event', + 'type' => MENU_CALLBACK, + 'access callback' => TRUE, + 'page arguments' => array(FB_PATH_AJAX_EVENT_ARGS), + ); + + // "Channel" http://developers.facebook.com/docs/reference/javascript/FB.init + $items['fb/channel'] = array( + 'page callback' => 'fb_channel_page', + 'type' => MENU_CALLBACK, + 'access callback' => TRUE, + ); + + return $items; +} + +/** + * Implementation of a %wildcard_load(). http://drupal.org/node/224170 + * + * Seems to get called a lot(!) so we cache. + */ +function fb_load($id) { + $cache = &drupal_static(__FUNCTION__); + if (!isset($cache)) + $cache = array(); + if (!isset($cache[$id])) { + $query = array('label' => $id); + if (fb_is_fb_admin_page()) { + // Show disabled apps to admins. + $query['status'] = 0; // status >= 0 + } + $cache[$id] = fb_get_app($query); + } + return $cache[$id]; +} + +/** + * Implements hook_permission(). + */ +function fb_permission() { + return array( + FB_PERM_ADMINISTER => array( + 'title' => t('Administer Facebook settings in fb.module'), + ), + ); +} + + +/** + * Implements hook_exit(). + * + * When completing a canvas page we need special processing for the session. See fb_session.inc. + * + * Also invoke hook_fb(FB_OP_EXIT), so that other modules can handle special + * cases (in particular form support in b_canvas.module. + */ +function fb_exit($destination = NULL) { + global $_fb_app, $_fb; + + if ($_fb_app && $_fb) { + + // Invoke other modules. + fb_invoke(FB_OP_EXIT, array('fb_app' => $_fb_app, + 'fb' => $GLOBALS['_fb']), + $destination); + } +} + + +/** + * Invoke hook_fb. + */ +function fb_invoke($op, $data = NULL, $return = NULL, $hook = FB_HOOK) { + foreach (module_implements($hook) as $name) { + $function = $name . '_' . $hook; + try { + $function($op, $data, $return); + } + catch (Exception $e) { + if (isset($data['fb_app'])) { + fb_log_exception($e, t('Exception calling %function(%op) (!app)', array( + '%function' => $function, + '%op' => $op, + '%label' => $data['fb_app']->label, + '%apikey' => $data['fb_app']->apikey, + '!app' => l($data['fb_app']->label, FB_PATH_ADMIN_APPS . '/' . $data['fb_app']->label), + ))); + } + else { + fb_log_exception($e, t('Exception calling %function(%op)', array( + '%function' => $function, + '%op' => $op))); + } + } + } + return $return; +} + +/** + * This method will clean up URLs. When serving canvas pages, extra + * information is included in URLs. This will remove the extra + * information. Useful when linking back to the website from a canvas page or + * wall post. + * + * For example in the following code, $url2 will link out to the server's domain: + * + * $url = url('node/42', array('absolute' => TRUE)); // i.e. http://apps.facebook.com/example/node/42 + * $url2 = fb_scrub_urls($url); // i.e. http://example.com/node/42 + * + * + * @see fb_url_rewrite.inc + */ +function fb_scrub_urls($content) { + if (function_exists('_fb_settings_url_rewrite_prefixes')) { + foreach (_fb_settings_url_rewrite_prefixes() as $key) { + $patterns[] = "|$key/[^/]*/|"; + $replacements[] = ""; + } + $content = preg_replace($patterns, $replacements, $content); + } + return $content; +} + +/** + * Convenience function to log and report exceptions. + */ +function fb_log_exception($e, $text = '', $fb = NULL) { + if ($text) + $message = $text . ': ' . $e->getMessage(); + else + $message = $e->getMessage(); + $message .= ' ' . $e->getCode(); + + if ($fb) { + $message .= '. (' . t('logged into facebook as %fbu', array('%fbu' => $fb->getUser())) . ')'; + } + if (fb_verbose()) { + $message .= '
' . $e . '
'; + } + watchdog('fb', $message, array(), WATCHDOG_ERROR); + if (user_access(FB_PERM_ADMINISTER)) { + drupal_set_message($message, 'error'); + } +} + +/** + * Exception handler for PHP5 exceptions. + */ + function fb_handle_exception($exception) { + $message = t('Facebook API exception %message. !trace', array( + '%message' => $exception->getMessage(), + '!trace' => '
' . $exception->getTraceAsString() . '
', + )); + watchdog('fb', $message, array(), WATCHDOG_ERROR); + //drupal_set_message($message, 'error'); + print $message; + + print "
\$_REQUEST:\n";
+  print_r($_REQUEST);
+  print "\n\nREQUEST_URI:\n" . request_uri();
+  print "
"; + +} + +/** + * Simple wrapper around $fb->api() which caches data. Does not support the + * polymorphic arguments of $fb->api(). This is not a replacement for that + * function. + * + * This is intended to avoid performace problems when, for example, + * $fb->api('me') is called several times in a single request. + * + * @param $graph_path + * Something facebook's graph API will understand. Could be an ID or a path like 'me/accounts', for example. + * + * @param $params + * Extras to pass to the graph API. When making a request that requires a + * token, try array('access_token' => fb_get_token()). + */ +function fb_api($graph_path, $params = array()) { + static $cache; + $fb = $GLOBALS['_fb']; + if (!$fb) { + return; + } + if (!isset($cache)) { + $cache = array(); + } + if (!isset($cache[$graph_path])) { + $cache[$graph_path] = $fb->api($graph_path, $params); + } + return $cache[$graph_path]; +} + +/** + * DEPRECATED. Use fb_api() instead. + * Returns information about one or more facebook users. + * + * Historically, this helper function used facebook's users_getInfo API, hence + * the name. Now it uses fql.query, but accomplishes the same thing. + * + * @param $oids + * Array of facebook object IDs. In this case they should each be a user id. + * + * @param $fb + * Rarely needed. For cases when global $_fb is not set, or more than one + * facebook api has been initialized. + * + * @param $refresh_cache + * If true, force a call to facebook instead of relying on temporarily stored + * data. + */ +function fb_users_getInfo($oids, $fb = NULL, $refresh_cache = FALSE) { + if (!$fb) { + $fb = $GLOBALS['_fb']; + } + $infos = array(); + + if (!is_array($oids)) + $oids = array(); + + if ($fb) { + $app_id = $fb->getAppId(); + // First try cache + if (!$refresh_cache && isset($_SESSION['fb'])) { + foreach ($oids as $oid) { + if (isset($_SESSION['fb'][$app_id]['userinfo'][$oid])) { + $info = $_SESSION['fb'][$app_id]['userinfo'][$oid]; + $infos[] = $info; + } + } + } + if (count($infos) != count($oids)) { + // Session cache did not include all users, update the cache. + $fields = array( + 'about_me', + 'affiliations', + 'name', + 'is_app_user', + 'pic', + 'pic_big', + 'pic_square', + 'profile_update_time', + 'proxied_email', + 'status', + 'email_hashes', + 'email', + 'uid', + ); + try { + $infos = fb_fql_query($fb, 'SELECT ' . implode(', ', $fields) . ' FROM user WHERE uid in(' . implode(', ', $oids) . ')', array(fb_get_token($fb))); + // Update cache with recent results. + if (is_array($infos)) { + foreach ($infos as $info) { + $_SESSION['fb'][$app_id]['userinfo'][$info['uid']] = $info; + } + } + } catch (FacebookApiException $e) { + fb_log_exception($e, t('Failed to query facebook user info'), $fb); + } + } + + return $infos; + } +} + +/** + * For debugging, add $conf['fb_verbose'] = TRUE; to settings.php. + */ +function fb_verbose() { + return variable_get(FB_VAR_VERBOSE, NULL); +} + +/** + * This function will be replaced, hopefully, by format_username in D7. + * + * @see http://drupal.org/node/192056 + */ +function fb_format_username($account) { + $name = !empty($account->name) ? $account->name : variable_get('anonymous', t('Anonymous')); + drupal_alter('username', $name, $account); + return $name; +} + +/** + * hook_username_alter(). + * + * Return a user's facebook name, instead of local username. + * @TODO cache, because drupal doesn't, and calls this often. + */ +function fb_username_alter(&$name, $account) { + // This function can be called very early in the bootstrap process, before + // the modules are initialized, in which case we will fail to alter the + // name. + if ($GLOBALS['_fb'] && $fbu = fb_get_fbu($account)) { // @TODO - is fb_get_fbu() a performance hit here? + try { + // Use fql query instead of graph api, because it will succeed more often. + $data = fb_fql_query($GLOBALS['_fb'], "SELECT name FROM user WHERE uid=$fbu", array( + 'access_token' => fb_get_token($GLOBALS['_fb']), + )); + if (count($data) && isset($data[0]['name'])) { + $name = $data[0]['name']; + } + } + catch (Exception $e) { + fb_log_exception($e, t('Failed to alter username for facebook user %fbu', array( + '%fbu' => $fbu))); + } + } +} + +/** + * Implements hook_theme(). + * + * Returns description of theme functions. + * + * @see fb.theme.inc + */ +function fb_theme() { + return array( + 'fb_username' => array( + 'arguments' => array( + 'fbu' => NULL, + 'object' => NULL, + 'orig' => NULL, + ), + 'file' => 'fb.theme.inc', + ), + 'fb_user_picture' => array( + 'arguments' => array( + 'fbu' => NULL, + 'account' => NULL, + 'orig' => NULL, + ), + 'file' => 'fb.theme.inc', + ), + 'fb_fbml_popup' => array( + 'arguments' => array('elements' => NULL), + 'file' => 'fb.theme.inc', + ), + 'fb_login_button' => array( + 'arguments' => array( + 'text' => 'Connect with Facebook', + 'options' => NULL), + 'file' => 'fb.theme.inc', + ), + ); +} + +//// Javascript and Ajax helpers + +/** + * Ajax javascript callback. + * + * For sites which use ajax, various events may create javascript which is + * normally embedded in a page. For example, posting to a user's wall. When + * ajax is used instead of a page reload, this callback will provide any + * javascript which should be run. + */ +function fb_js_cb() { + $js_array = fb_invoke(FB_OP_JS, array('fb' => $GLOBALS['_fb'], 'fb_app' => $GLOBALS['_fb_app']), array()); + $extra_js = implode("\n", $extra); + print $extra_js; + exit(); +} + +/** + * Ajax callback handles an event from facebook's javascript sdk. + * + * @see + * fb.js and + * http://developers.facebook.com/docs/reference/javascript/FB.Event.subscribe + * + * @return + * Array of javascript to be evaluated by the page which called this + * callback. + */ +function fb_ajax_event($event_type) { + global $_fb, $_fb_app; + $js_array = array(); + + if (isset($_REQUEST['apikey'])) { + $_fb_app = fb_get_app(array('apikey' => $_REQUEST['apikey'])); + if ($_fb_app) { + $_fb = fb_api_init($_fb_app); + // Data to pass to hook_fb. + $data = array( + 'fb_app' => $_fb_app, + 'fb' => $_fb, + 'event_type' => $event_type, + 'event_data' => $_POST, // POSTed via ajax. + ); + + $js_array = fb_invoke(FB_OP_AJAX_EVENT, $data, array()); + + } + else { + watchdog('fb', 'fb_ajax_event did not find application %id', array('%id' => $_REQUEST['apikey']), WATCHDOG_ERROR); + } + + if ($event_type == 'session_change') { + // Session change is a special case. If user has logged out of + // facebook, we want a new drupal session. We do this here, even if + // fb_user.module is not enabled. + if (!isset($_POST['fbu']) || !$_POST['fbu']) { // Logout, not login. + _fb_logout(); + } + } + + } + else { + watchdog('fb', 'fb_ajax_event called badly. Not passed apikey.', array(), WATCHDOG_ERROR); + // Trying to track down what makes this happen. + if (fb_verbose() == 'extreme') { + watchdog('fb', 'fb_ajax_event called badly. Not passed apikey. trace: !trace', array( + '!trace' => '
' . print_r(debug_backtrace(), 1) . '
', + ), WATCHDOG_ERROR); + } + } + drupal_json_output($js_array); + exit(); +} + +/** + * Menu callback for custom channel. + * + * @see http://developers.facebook.com/docs/reference/javascript/FB.init + */ +function fb_channel_page() { + //headers instructing browser to cache this page. + // Do these work? + drupal_add_http_header("Cache-Control", "public"); + drupal_add_http_header("Expires", "Sun, 17-Jan-2038 19:14:07 GMT"); + + $date = format_date(time()); + $output = "\n"; + $url = fb_js_settings('js_sdk_url'); + $output .= "\n"; + print $output; + exit(); +} + +//// Miscellaneous helpers and convenience functions. + +/** + * Protocol (http or https) of the current request. + */ +function fb_protocol() { + return (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on') ? 'https' : 'http'; +} + + +/** + * Convenience wrapper around drupal_access_denied(). Call on pages where the + * access is denied because the user is not logged into facebook. + */ +function fb_access_denied() { + if (!fb_facebook_user()) { + drupal_set_message(t('You must log into facebook to view this page.')); + } + drupal_access_denied(); + exit(); +} + diff --git a/fb_app.info b/fb_app.info index dca0d2a..fa8b79b 100644 --- a/fb_app.info +++ b/fb_app.info @@ -1,10 +1,16 @@ -name = Facebook Apps -description = (fb_app.module) Host and administer Facebook apps. -package = Drupal for Facebook -core = 7.x - -dependencies[] = fb - -files[] = fb_app.module -files[] = fb_app.install -files[] = fb_app.admin.inc \ No newline at end of file +name = Facebook Apps +description = (fb_app.module) Host and administer Facebook apps. +package = Drupal for Facebook +core = 7.x + +dependencies[] = fb + +files[] = fb_app.module +files[] = fb_app.install +files[] = fb_app.admin.inc +; Information added by drupal.org packaging script on 2011-06-23 +version = "7.x-4.x-dev" +core = "7.x" +project = "fb" +datestamp = "1308787966" + diff --git a/fb_canvas.info b/fb_canvas.info index 1a5da5c..52d193f 100644 --- a/fb_canvas.info +++ b/fb_canvas.info @@ -1,12 +1,19 @@ -name = FB Canvas Pages -description = (fb_canvas.module) Host apps on facebook.com using iframe canvas pages. -package = Drupal for Facebook -core = 7.x - -dependencies[] = fb -dependencies[] = fb_app - -files[] = fb_canvas.module -files[] = fb_canvas.admin.inc -files[] = fb_url_rewrite.inc - +name = FB Canvas Pages +description = (fb_canvas.module) Host apps on facebook.com using iframe canvas pages. +package = Drupal for Facebook +core = 7.x + +dependencies[] = fb +dependencies[] = fb_app + +files[] = fb_canvas.module +files[] = fb_canvas.admin.inc +files[] = fb_url_rewrite.inc + + +; Information added by drupal.org packaging script on 2011-06-23 +version = "7.x-4.x-dev" +core = "7.x" +project = "fb" +datestamp = "1308787966" + diff --git a/fb_connect.info b/fb_connect.info index 632b6b1..91b7a27 100644 --- a/fb_connect.info +++ b/fb_connect.info @@ -1,11 +1,17 @@ -name = Facebook Connect -description = (fb_connect.module) Host Facebook Connect apps. Login buttons and FBML helpers that are useful on iframe canvas pages as well. -package = Drupal for Facebook -core = 7.x - -dependencies[] = fb -dependencies[] = fb_app - -files[] = fb_connect.admin.inc -files[] = fb_connect.install -files[] = fb_connect.module \ No newline at end of file +name = Facebook Connect +description = (fb_connect.module) Host Facebook Connect apps. Login buttons and FBML helpers that are useful on iframe canvas pages as well. +package = Drupal for Facebook +core = 7.x + +dependencies[] = fb +dependencies[] = fb_app + +files[] = fb_connect.admin.inc +files[] = fb_connect.install +files[] = fb_connect.module +; Information added by drupal.org packaging script on 2011-06-23 +version = "7.x-4.x-dev" +core = "7.x" +project = "fb" +datestamp = "1308787966" + diff --git a/fb_connect.module b/fb_connect.module index 27dbabb..01ffa77 100644 --- a/fb_connect.module +++ b/fb_connect.module @@ -1,457 +1,457 @@ - 'Facebook Connect', - 'description' => 'Configure Facebook Connect', - 'page callback' => 'drupal_get_form', - 'page arguments' => array('fb_connect_admin_settings'), - 'access arguments' => array(FB_PERM_ADMINISTER), - 'file' => 'fb_connect.admin.inc', - 'type' => MENU_LOCAL_TASK, - ); - - - return $items; -} - - -/** - * Prepare for fbConnect use. Because a single Drupal might support - * multiple apps, we don't know in advance which is the fbConnect app. - */ -function fb_connect_app_init($fb_app) { - if (isset($GLOBALS['_fb_app']) && - $GLOBALS['_fb_app']->apikey != $fb_app->apikey) { - // If we're in an iframe, only support connect for the iframe app. - return; - } - - if ($fb = fb_api_init($fb_app)) { - $fbu = $fb->getUser(); - if ($fbu && - (!isset($GLOBALS['_fb_app']) || $GLOBALS['_fb_app']->apikey != $fb_app->apikey)) { - // The user has authorized the app and we now know something about them. Use a hook to trigger the actions of other modules. - fb_invoke(FB_OP_APP_IS_AUTHORIZED, array( - 'fbu' => $fbu, - 'fb_app' => $fb_app, - 'fb' => $fb)); - } - - // Remember which app we've initialized. - _fb_connect_set_app($fb_app); - _fb_connect_add_js($fb_app, $fb); - } - return $fb; -} - -/** - * Helper function for other modules to know page is connected. - * - * Note that this may return data on connect pages and in iframe apps - * (depending on how iframe is configured). - */ -function fb_connect_get_app() { - return _fb_connect_set_app(); -} -function _fb_connect_set_app($fb_app = NULL) { - $cache = &drupal_static(__FUNCTION__); - if (isset($fb_app)) { - $cache = $fb_app; - } - return $cache; -} - -/** - * Implements hook_fb(). - */ -function fb_connect_fb($op, $data, &$return) { - if ($op == FB_OP_CURRENT_APP && !$return && !fb_is_canvas()) { - // This will cause fb.module to set the global $_fb when user is logged in via fbConnect. - if ($apikey = variable_get(FB_VAR_APIKEY, NULL)) { - // Use $conf['fb_apikey'] if set in settings.php. - $return = fb_get_app(array('apikey' => $apikey)); - } - elseif ($label = variable_get(FB_CONNECT_VAR_PRIMARY, NULL)) { - $return = fb_get_app(array('label' => $label)); - } - } - elseif ($op == FB_OP_POST_INIT) { - if (!fb_is_tab()) { - // Init Facebook javascript for primary app - _fb_connect_add_js($data['fb_app'], $data['fb']); - } - - // Include our admin hooks. - if (fb_is_fb_admin_page()) { - module_load_include('inc', 'fb_connect', 'fb_connect.admin'); - } - } -} - -/** - * This wrapper function around drupal_add_js() ensures that our - * settings are added once and only once when needed. - * - */ -function _fb_connect_add_js($fb_app, $fb) { - $just_once = &drupal_static(__FUNCTION__); - - if (fb_is_tab()) - // Tabs are FBML. - return; - - if (!isset($just_once)) { - drupal_add_js(array( - 'fb_connect' => array( - 'front_url' => url(''), - 'fbu' => fb_facebook_user(), - 'uid' => $GLOBALS['user']->uid, - ), - ), 'setting'); - drupal_add_js(drupal_get_path('module', 'fb_connect') . '/fb_connect.js'); - $just_once = TRUE; - } - - // If we are not the global $_fb_app - if ($fb_app) { - $settings = fb_js_settings(); - if (!isset($settings['fb_init_settings']['apiKey']) || - $settings['fb_init_settings']['apiKey'] != $fb_app->apikey) { - // Ensure JS initializes with the proper apikey. We may reach this if - // there is no "primary" app. - // @TODO fb.module should have a helper to make this cleaner. - - $settings['fb_init_settings']['apiKey'] = $fb_app->apikey; - $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. - } - } - -} - - -/** - * Default markup for our login block. - */ -function _fb_connect_block_login_defaults() { - return array('anon_not_connected' => array( - 'title' => t('Facebook Connect'), - 'body' => array('value' => 'Connect'), - ), - 'user_not_connected' => array( - 'title' => t('Facebook Connect'), - 'body' => array('value' => 'Connect'), - ), - 'connected' => array( - 'title' => t('Facebook Connect'), - // Logout commented out, because drupal has logout link. - 'body' => array('value' => ''), - ), - ); -} - -/** - * Implements hook_block_info(). - */ -function fb_connect_block_info() { - $items = array(); - foreach (fb_get_all_apps() as $fb_app) { - $d = 'login_' . $fb_app->label; - $items[$d] = array( - 'info' => t('Facebook Connect Login to !app', - array('!app' => $fb_app->title)), - ); - } - return $items; -} - -/** - * Implements hook_block_configure(). - */ -function fb_connect_block_configure($delta = '') { - $orig_defaults = _fb_connect_block_login_defaults(); - $defaults = variable_get('fb_connect_block_' . $delta, $orig_defaults); - $form['config'] = array('#tree' => TRUE); - - // Settings for each user status that we can detect. - foreach (array('anon_not_connected', 'user_not_connected', 'connected') as $key) { - $form['config'][$key] = array( - '#type' => 'fieldset', - // title and description below - '#collapsible' => TRUE, - '#collapsed' => FALSE, - ); - $form['config'][$key]['title'] = array( - '#type' => 'textfield', - '#title' => t('Default title'), - '#default_value' => $defaults[$key]['title'], - ); - $textformat = isset($defaults[$key]['body']['format']) ? $defaults[$key]['body']['format'] : 'full_html'; - $form['config'][$key]['body'] = array( - '#type' => 'text_format', - '#title' => t('Body'), - '#base_type' => 'textarea', - '#format' => $textformat, - '#default_value' => $defaults[$key]['body']['value'], - ); - } - - $form['config'][] = array( - '#markup' => "

Be sure to select a format that allows XFBML tags! (That is, use Full HTML or PHP code (PHP Filter module must be enabled), rather than Filtered HTML.)

", - '#weight' => -10, - ); - $form['config']['anon_not_connected']['#title'] = t('Anonymous user, not connected'); - $form['config']['anon_not_connected']['#description'] = t('Settings when local user is Anonymous, and not connected to Facebook. Typically a new account will be created when the user clicks the connect button.'); - $form['config']['anon_not_connected']['body']['#description'] = t('Suggestion: %default .', array('%default' => $orig_defaults['anon_not_connected']['body']['value'])); - - $form['config']['user_not_connected']['#title'] = t('Registered user, not connected'); - $form['config']['user_not_connected']['#description'] = t('Settings when local user is registered, and not connected to Facebook. Typically the facebook id will be linked to the local id after the user clicks the connect button.'); - $form['config']['user_not_connected']['body']['#description'] = t('Suggestion: %default .', array('%default' => $orig_defaults['user_not_connected']['body']['value'])); - $form['config']['connected']['#title'] = t('Connected user'); - $form['config']['connected']['#description'] = t('Settings when local user is connected to Facebook. You may render facebook\'s logout button, and/or information about the user. Consider using XFBML such as <fb:name uid=!fbu></fb:name> or <fb:profile-pic uid=!fbu></fb:profile-pic>', array('xfbml_url' => 'http://wiki.developers.facebook.com/index.php/XFBML')); - $form['config']['connected']['body']['#description'] .= t('Note that !fbu will be replaced with the user\'s facebook id.
Suggestion: %default .', array('%default' => $orig_defaults['connected']['body']['value'])); - - return $form; -} - -/** - * Implements hook_block_save(). - */ -function fb_connect_block_save($delta = '', $edit = array()) { - variable_set('fb_connect_block_' . $delta, $edit['config']); -} - -function fb_connect_block_view($delta = '') { - if (!fb_is_tab()) { - // Hide block on tabs, where the $fbu is actually the page, not the user. - if (strpos($delta, 'login_') === 0) { - // Login block - $label = substr($delta, 6); // length of 'login_' - $fb_app = fb_get_app(array('label' => $label)); - if ($fb_app && - ($fb = fb_connect_app_init($fb_app))) { - $fbu = $fb->getUser(); - - //_fb_connect_add_js($fb_app); moved to fb_connect_app_init() - - $base = drupal_get_path('module', 'fb_connect'); - - $config = variable_get('fb_connect_block_' . $delta, _fb_connect_block_login_defaults()); - if ($fbu) { - $key = 'connected'; - } - elseif ($GLOBALS['user']->uid >= 1) { - $key = 'user_not_connected'; - } - else { - $key = 'anon_not_connected'; - } - - $subject = $config[$key]['title']; - $content = $config[$key]['body']['value']; - - // substitute fbu - $content = str_replace('!fbu', $fbu, $content); - - // substitute perms - $perms = array(); - drupal_alter('fb_required_perms', $perms); - $content = str_replace('!perms', implode(',', $perms), $content); - - // Filter output according to settings in block configuration - $subject = check_plain($subject); - if (isset($config[$key]['body']['format'])) { - $content = check_markup($content, $config[$key]['body']['format'], '', FALSE); - } - - $block = array( - 'subject' => $subject, - 'content' => $content, - ); - return $block; - } - } - } -} - -/** - * Helper returns configuration for this module, on a per-app basis. - */ -function _fb_connect_get_config($fb_app) { - $fb_app_data = fb_get_app_data($fb_app); - $config = isset($fb_app_data['fb_connect']) ? $fb_app_data['fb_connect'] : array(); - - // Merge in defaults - $config += array( - - ); - return $config; -} - -/** - * Implements hook_form_alter(). - */ -function fb_connect_form_alter(&$form, &$form_state, $form_id) { - // Add our settings to the fb_app edit form. - if (isset($form['fb_app_data'])) { - $fb_app = $form['#fb_app']; - - $form['fb_app_data']['fb_connect'] = array( - '#type' => 'fieldset', - '#title' => 'Facebook connect', - '#tree' => TRUE, - '#collapsible' => TRUE, - '#collapsed' => $fb_app->label ? TRUE : FALSE, - ); - - // "Primary" will be initialized on every non-canvas page. - $primary_label = variable_get(FB_CONNECT_VAR_PRIMARY, NULL); - $form['fb_app_data']['fb_connect']['primary'] = array( - '#type' => 'checkbox', - '#title' => t('Primary'), - '#description' => t('Initialize fbConnect javascript on all (non-canvas) pages. If this site supports multiple Facebook Apps, this may be checked for at most one.'), - '#default_value' => isset($fb_app->label) && ($primary_label == $fb_app->label), - ); - if (($primary_label) && ($primary_label != $fb_app->label)) { - $form['fb_app_data']['fb_connect']['primary']['#description'] .= '
' . - t('Note that checking this will replace %app as the primary Facebook Connect app.', array('%app' => $primary_label)); - } - $form['buttons']['submit']['#submit'][] = 'fb_connect_app_submit'; - } -} - -/** - * Submit callback. Sets or unsets "primary" app. - */ -function fb_connect_app_submit($form, &$form_state) { - $values = $form_state['values']; - $label = $values['label']; - $data = $values['fb_app_data']['fb_connect']; - if ($data['primary']) { - variable_set(FB_CONNECT_VAR_PRIMARY, $label); - drupal_set_message(t('%label is the primary Facebook Connect application.', array('%label' => $label))); - } - elseif ($label == variable_get(FB_CONNECT_VAR_PRIMARY, NULL)) { - // This app was the primary one, but the user has unchecked it. - variable_set(FB_CONNECT_VAR_PRIMARY, NULL); - } -} - - -/** - * Implements hook_theme_registry_alter(). - * - * Override theme functions for things that can be displayed using - * XFBML. Currently overriding username and user_picture. We rename - * the original entries, as we will use them for users without - * javascript enabled. - * - * This hook is not well documented. Who knows what its supposed to - * return? No doubt this will need updating with each new version of - * Drupal. - */ -function fb_connect_theme_registry_alter(&$theme_registry) { - // Ideally, we'd do this only on themes which will certainly be used for facebook connect pages. - if (variable_get(FB_CONNECT_VAR_THEME_USERNAME_2, TRUE) || - (variable_get(FB_CONNECT_VAR_THEME_USERNAME_1, TRUE) && - $theme_registry['username']['type'] == 'module')) { - // Re-register the original theme function under a new name. - $theme_registry['fb_connect_username_orig'] = $theme_registry['username']; - // Override theme username - $theme_registry['username'] = array( - 'variables' => array('object' => NULL), - 'function' => 'fb_connect_theme_username_override', - 'type' => 'module', - 'theme path' => drupal_get_path('module', 'fb_connect'), // something is needed here but it isn't used - ); - } - - if (variable_get(FB_CONNECT_VAR_THEME_USERPIC_2, TRUE) || - (variable_get(FB_CONNECT_VAR_THEME_USERPIC_1, TRUE) && - $theme_registry['user_picture']['type'] == 'module')) { - // Re-register the original theme function under a new name. - $theme_registry['fb_connect_user_picture_orig'] = $theme_registry['user_picture']; - // Override theme username - $theme_registry['user_picture'] = array( - 'variables' => array('account' => NULL), - 'function' => 'fb_connect_theme_user_picture_override', - 'type' => 'module', - 'theme path' => drupal_get_path('module', 'fb_connect'), // something is needed here but it isn't used - ); - } -} - -/** - * Our replacement for theme('user_picture', ...) - */ -function fb_connect_theme_user_picture_override($variables) { - $account = $variables['account']; - // Markup without fb_connect. - $orig = theme('fb_connect_user_picture_orig', array('account' => $account)); - - // Respect Drupal's profile pic, if uploaded. - if (isset($account->picture) && $account->picture) { - return $orig; - } - - if ($fbu = fb_get_object_fbu($account)) { - $output = theme('fb_user_picture', array( - 'fbu' => $fbu, - 'account' => $account, - 'orig' => $orig, - )); - } - else { - $output = $orig; - } - return $output; -} - -/** - * Our replacement for theme('username', ...) - */ -function fb_connect_theme_username_override($variables) { - $account = $variables['account']; - $orig = theme('fb_connect_username_orig', $variables); - - if ($fbu = fb_get_object_fbu($account)) { - // Theme the username with XFBML, using original username as backup. - return theme('fb_username', array( - 'fbu' => $fbu, - 'account' => $account, - 'orig' => $orig, - )); - } - else { - return $orig; - } -} - - - + 'Facebook Connect', + 'description' => 'Configure Facebook Connect', + 'page callback' => 'drupal_get_form', + 'page arguments' => array('fb_connect_admin_settings'), + 'access arguments' => array(FB_PERM_ADMINISTER), + 'file' => 'fb_connect.admin.inc', + 'type' => MENU_LOCAL_TASK, + ); + + + return $items; +} + + +/** + * Prepare for fbConnect use. Because a single Drupal might support + * multiple apps, we don't know in advance which is the fbConnect app. + */ +function fb_connect_app_init($fb_app) { + if (isset($GLOBALS['_fb_app']) && + $GLOBALS['_fb_app']->apikey != $fb_app->apikey) { + // If we're in an iframe, only support connect for the iframe app. + return; + } + + if ($fb = fb_api_init($fb_app)) { + $fbu = $fb->getUser(); + if ($fbu && + (!isset($GLOBALS['_fb_app']) || $GLOBALS['_fb_app']->apikey != $fb_app->apikey)) { + // The user has authorized the app and we now know something about them. Use a hook to trigger the actions of other modules. + fb_invoke(FB_OP_APP_IS_AUTHORIZED, array( + 'fbu' => $fbu, + 'fb_app' => $fb_app, + 'fb' => $fb)); + } + + // Remember which app we've initialized. + _fb_connect_set_app($fb_app); + _fb_connect_add_js($fb_app, $fb); + } + return $fb; +} + +/** + * Helper function for other modules to know page is connected. + * + * Note that this may return data on connect pages and in iframe apps + * (depending on how iframe is configured). + */ +function fb_connect_get_app() { + return _fb_connect_set_app(); +} +function _fb_connect_set_app($fb_app = NULL) { + $cache = &drupal_static(__FUNCTION__); + if (isset($fb_app)) { + $cache = $fb_app; + } + return $cache; +} + +/** + * Implements hook_fb(). + */ +function fb_connect_fb($op, $data, &$return) { + if ($op == FB_OP_CURRENT_APP && !$return && !fb_is_canvas()) { + // This will cause fb.module to set the global $_fb when user is logged in via fbConnect. + if ($apikey = variable_get(FB_VAR_APIKEY, NULL)) { + // Use $conf['fb_apikey'] if set in settings.php. + $return = fb_get_app(array('apikey' => $apikey)); + } + elseif ($label = variable_get(FB_CONNECT_VAR_PRIMARY, NULL)) { + $return = fb_get_app(array('label' => $label)); + } + } + elseif ($op == FB_OP_POST_INIT) { + if (!fb_is_tab()) { + // Init Facebook javascript for primary app + _fb_connect_add_js($data['fb_app'], $data['fb']); + } + + // Include our admin hooks. + if (fb_is_fb_admin_page()) { + module_load_include('inc', 'fb_connect', 'fb_connect.admin'); + } + } +} + +/** + * This wrapper function around drupal_add_js() ensures that our + * settings are added once and only once when needed. + * + */ +function _fb_connect_add_js($fb_app, $fb) { + $just_once = &drupal_static(__FUNCTION__); + + if (fb_is_tab()) + // Tabs are FBML. + return; + + if (!isset($just_once)) { + drupal_add_js(array( + 'fb_connect' => array( + 'front_url' => url(''), + 'fbu' => fb_facebook_user(), + 'uid' => $GLOBALS['user']->uid, + ), + ), 'setting'); + drupal_add_js(drupal_get_path('module', 'fb_connect') . '/fb_connect.js'); + $just_once = TRUE; + } + + // If we are not the global $_fb_app + if ($fb_app) { + $settings = fb_js_settings(); + if (!isset($settings['fb_init_settings']['apiKey']) || + $settings['fb_init_settings']['apiKey'] != $fb_app->apikey) { + // Ensure JS initializes with the proper apikey. We may reach this if + // there is no "primary" app. + // @TODO fb.module should have a helper to make this cleaner. + + $settings['fb_init_settings']['apiKey'] = $fb_app->apikey; + $settings['fb_init_settings']['access_token'] = $fb->getAccessToken(); + 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. + } + } + +} + + +/** + * Default markup for our login block. + */ +function _fb_connect_block_login_defaults() { + return array('anon_not_connected' => array( + 'title' => t('Facebook Connect'), + 'body' => array('value' => 'Connect'), + ), + 'user_not_connected' => array( + 'title' => t('Facebook Connect'), + 'body' => array('value' => 'Connect'), + ), + 'connected' => array( + 'title' => t('Facebook Connect'), + // Logout commented out, because drupal has logout link. + 'body' => array('value' => ''), + ), + ); +} + +/** + * Implements hook_block_info(). + */ +function fb_connect_block_info() { + $items = array(); + foreach (fb_get_all_apps() as $fb_app) { + $d = 'login_' . $fb_app->label; + $items[$d] = array( + 'info' => t('Facebook Connect Login to !app', + array('!app' => $fb_app->title)), + ); + } + return $items; +} + +/** + * Implements hook_block_configure(). + */ +function fb_connect_block_configure($delta = '') { + $orig_defaults = _fb_connect_block_login_defaults(); + $defaults = variable_get('fb_connect_block_' . $delta, $orig_defaults); + $form['config'] = array('#tree' => TRUE); + + // Settings for each user status that we can detect. + foreach (array('anon_not_connected', 'user_not_connected', 'connected') as $key) { + $form['config'][$key] = array( + '#type' => 'fieldset', + // title and description below + '#collapsible' => TRUE, + '#collapsed' => FALSE, + ); + $form['config'][$key]['title'] = array( + '#type' => 'textfield', + '#title' => t('Default title'), + '#default_value' => $defaults[$key]['title'], + ); + $textformat = isset($defaults[$key]['body']['format']) ? $defaults[$key]['body']['format'] : 'full_html'; + $form['config'][$key]['body'] = array( + '#type' => 'text_format', + '#title' => t('Body'), + '#base_type' => 'textarea', + '#format' => $textformat, + '#default_value' => $defaults[$key]['body']['value'], + ); + } + + $form['config'][] = array( + '#markup' => "

Be sure to select a format that allows XFBML tags! (That is, use Full HTML or PHP code (PHP Filter module must be enabled), rather than Filtered HTML.)

", + '#weight' => -10, + ); + $form['config']['anon_not_connected']['#title'] = t('Anonymous user, not connected'); + $form['config']['anon_not_connected']['#description'] = t('Settings when local user is Anonymous, and not connected to Facebook. Typically a new account will be created when the user clicks the connect button.'); + $form['config']['anon_not_connected']['body']['#description'] = t('Suggestion: %default .', array('%default' => $orig_defaults['anon_not_connected']['body']['value'])); + + $form['config']['user_not_connected']['#title'] = t('Registered user, not connected'); + $form['config']['user_not_connected']['#description'] = t('Settings when local user is registered, and not connected to Facebook. Typically the facebook id will be linked to the local id after the user clicks the connect button.'); + $form['config']['user_not_connected']['body']['#description'] = t('Suggestion: %default .', array('%default' => $orig_defaults['user_not_connected']['body']['value'])); + $form['config']['connected']['#title'] = t('Connected user'); + $form['config']['connected']['#description'] = t('Settings when local user is connected to Facebook. You may render facebook\'s logout button, and/or information about the user. Consider using XFBML such as <fb:name uid=!fbu></fb:name> or <fb:profile-pic uid=!fbu></fb:profile-pic>', array('xfbml_url' => 'http://wiki.developers.facebook.com/index.php/XFBML')); + $form['config']['connected']['body']['#description'] .= t('Note that !fbu will be replaced with the user\'s facebook id.
Suggestion: %default .', array('%default' => $orig_defaults['connected']['body']['value'])); + + return $form; +} + +/** + * Implements hook_block_save(). + */ +function fb_connect_block_save($delta = '', $edit = array()) { + variable_set('fb_connect_block_' . $delta, $edit['config']); +} + +function fb_connect_block_view($delta = '') { + if (!fb_is_tab()) { + // Hide block on tabs, where the $fbu is actually the page, not the user. + if (strpos($delta, 'login_') === 0) { + // Login block + $label = substr($delta, 6); // length of 'login_' + $fb_app = fb_get_app(array('label' => $label)); + if ($fb_app && + ($fb = fb_connect_app_init($fb_app))) { + $fbu = $fb->getUser(); + + //_fb_connect_add_js($fb_app); moved to fb_connect_app_init() + + $base = drupal_get_path('module', 'fb_connect'); + + $config = variable_get('fb_connect_block_' . $delta, _fb_connect_block_login_defaults()); + if ($fbu) { + $key = 'connected'; + } + elseif ($GLOBALS['user']->uid >= 1) { + $key = 'user_not_connected'; + } + else { + $key = 'anon_not_connected'; + } + + $subject = $config[$key]['title']; + $content = $config[$key]['body']['value']; + + // substitute fbu + $content = str_replace('!fbu', $fbu, $content); + + // substitute perms + $perms = array(); + drupal_alter('fb_required_perms', $perms); + $content = str_replace('!perms', implode(',', $perms), $content); + + // Filter output according to settings in block configuration + $subject = check_plain($subject); + if (isset($config[$key]['body']['format'])) { + $content = check_markup($content, $config[$key]['body']['format'], '', FALSE); + } + + $block = array( + 'subject' => $subject, + 'content' => $content, + ); + return $block; + } + } + } +} + +/** + * Helper returns configuration for this module, on a per-app basis. + */ +function _fb_connect_get_config($fb_app) { + $fb_app_data = fb_get_app_data($fb_app); + $config = isset($fb_app_data['fb_connect']) ? $fb_app_data['fb_connect'] : array(); + + // Merge in defaults + $config += array( + + ); + return $config; +} + +/** + * Implements hook_form_alter(). + */ +function fb_connect_form_alter(&$form, &$form_state, $form_id) { + // Add our settings to the fb_app edit form. + if (isset($form['fb_app_data'])) { + $fb_app = $form['#fb_app']; + + $form['fb_app_data']['fb_connect'] = array( + '#type' => 'fieldset', + '#title' => 'Facebook connect', + '#tree' => TRUE, + '#collapsible' => TRUE, + '#collapsed' => $fb_app->label ? TRUE : FALSE, + ); + + // "Primary" will be initialized on every non-canvas page. + $primary_label = variable_get(FB_CONNECT_VAR_PRIMARY, NULL); + $form['fb_app_data']['fb_connect']['primary'] = array( + '#type' => 'checkbox', + '#title' => t('Primary'), + '#description' => t('Initialize fbConnect javascript on all (non-canvas) pages. If this site supports multiple Facebook Apps, this may be checked for at most one.'), + '#default_value' => isset($fb_app->label) && ($primary_label == $fb_app->label), + ); + if (($primary_label) && ($primary_label != $fb_app->label)) { + $form['fb_app_data']['fb_connect']['primary']['#description'] .= '
' . + t('Note that checking this will replace %app as the primary Facebook Connect app.', array('%app' => $primary_label)); + } + $form['buttons']['submit']['#submit'][] = 'fb_connect_app_submit'; + } +} + +/** + * Submit callback. Sets or unsets "primary" app. + */ +function fb_connect_app_submit($form, &$form_state) { + $values = $form_state['values']; + $label = $values['label']; + $data = $values['fb_app_data']['fb_connect']; + if ($data['primary']) { + variable_set(FB_CONNECT_VAR_PRIMARY, $label); + drupal_set_message(t('%label is the primary Facebook Connect application.', array('%label' => $label))); + } + elseif ($label == variable_get(FB_CONNECT_VAR_PRIMARY, NULL)) { + // This app was the primary one, but the user has unchecked it. + variable_set(FB_CONNECT_VAR_PRIMARY, NULL); + } +} + + +/** + * Implements hook_theme_registry_alter(). + * + * Override theme functions for things that can be displayed using + * XFBML. Currently overriding username and user_picture. We rename + * the original entries, as we will use them for users without + * javascript enabled. + * + * This hook is not well documented. Who knows what its supposed to + * return? No doubt this will need updating with each new version of + * Drupal. + */ +function fb_connect_theme_registry_alter(&$theme_registry) { + // Ideally, we'd do this only on themes which will certainly be used for facebook connect pages. + if (variable_get(FB_CONNECT_VAR_THEME_USERNAME_2, TRUE) || + (variable_get(FB_CONNECT_VAR_THEME_USERNAME_1, TRUE) && + $theme_registry['username']['type'] == 'module')) { + // Re-register the original theme function under a new name. + $theme_registry['fb_connect_username_orig'] = $theme_registry['username']; + // Override theme username + $theme_registry['username'] = array( + 'variables' => array('object' => NULL), + 'function' => 'fb_connect_theme_username_override', + 'type' => 'module', + 'theme path' => drupal_get_path('module', 'fb_connect'), // something is needed here but it isn't used + ); + } + + if (variable_get(FB_CONNECT_VAR_THEME_USERPIC_2, TRUE) || + (variable_get(FB_CONNECT_VAR_THEME_USERPIC_1, TRUE) && + $theme_registry['user_picture']['type'] == 'module')) { + // Re-register the original theme function under a new name. + $theme_registry['fb_connect_user_picture_orig'] = $theme_registry['user_picture']; + // Override theme username + $theme_registry['user_picture'] = array( + 'variables' => array('account' => NULL), + 'function' => 'fb_connect_theme_user_picture_override', + 'type' => 'module', + 'theme path' => drupal_get_path('module', 'fb_connect'), // something is needed here but it isn't used + ); + } +} + +/** + * Our replacement for theme('user_picture', ...) + */ +function fb_connect_theme_user_picture_override($variables) { + $account = $variables['account']; + // Markup without fb_connect. + $orig = theme('fb_connect_user_picture_orig', array('account' => $account)); + + // Respect Drupal's profile pic, if uploaded. + if (isset($account->picture) && $account->picture) { + return $orig; + } + + if ($fbu = fb_get_object_fbu($account)) { + $output = theme('fb_user_picture', array( + 'fbu' => $fbu, + 'account' => $account, + 'orig' => $orig, + )); + } + else { + $output = $orig; + } + return $output; +} + +/** + * Our replacement for theme('username', ...) + */ +function fb_connect_theme_username_override($variables) { + $account = $variables['account']; + $orig = theme('fb_connect_username_orig', $variables); + + if ($fbu = fb_get_object_fbu($account)) { + // Theme the username with XFBML, using original username as backup. + return theme('fb_username', array( + 'fbu' => $fbu, + 'account' => $account, + 'orig' => $orig, + )); + } + else { + return $orig; + } +} + + + diff --git a/fb_devel.info b/fb_devel.info index eac0cbb..7d01f46 100644 --- a/fb_devel.info +++ b/fb_devel.info @@ -1,12 +1,18 @@ -name = FB Development tools -description = (fb_devel.module) Catch errors and solve problems before they surprise you. Displays messages and blocks that help when developing and debugging Apps. Disable on your production server, when your site is live, because this module adds overhead. - -package = Drupal for Facebook - -core = 7.x - -dependencies[] = fb -dependencies[] = devel - -files[] = fb_devel.install -files[] = fb_devel.module \ No newline at end of file +name = FB Development tools +description = (fb_devel.module) Catch errors and solve problems before they surprise you. Displays messages and blocks that help when developing and debugging Apps. Disable on your production server, when your site is live, because this module adds overhead. + +package = Drupal for Facebook + +core = 7.x + +dependencies[] = fb +dependencies[] = devel + +files[] = fb_devel.install +files[] = fb_devel.module +; Information added by drupal.org packaging script on 2011-06-23 +version = "7.x-4.x-dev" +core = "7.x" +project = "fb" +datestamp = "1308787966" + diff --git a/fb_devel.module b/fb_devel.module index e1a536e..697ee01 100644 --- a/fb_devel.module +++ b/fb_devel.module @@ -1,825 +1,825 @@ - 'fb_devel_page', - 'type' => MENU_CALLBACK, - 'access callback' => TRUE, /* TODO: restrict access */ - ); - - $items['fb/devel/fbu'] = array( - 'page callback' => 'fb_devel_fbu_page', - 'type' => MENU_CALLBACK, - 'access callback' => TRUE, - ); - - $items['fb/devel/tab'] = array( - 'page callback' => 'fb_devel_tab', - 'type' => MENU_CALLBACK, - 'access callback' => TRUE, /* TODO: restrict access */ - ); - - // Return some info for debugging AHAH problems - $items['fb/devel/js'] = array( - 'page callback' => 'fb_devel_js', - 'type' => MENU_CALLBACK, - 'access callback' => TRUE, - ); - - $items['fb/devel/linter'] = array( - 'title' => 'Facebook linter', - 'page callback' => 'drupal_not_found', // Because we alter link, below. - 'type' => MENU_NORMAL_ITEM, - 'access arguments' => array('access devel information'), - 'menu_name' => 'devel', - 'options' => array('alter' => TRUE), - ); - - return $items; -} - -// Trick learned from devel.module, to make link to linter for the current page. -function fb_devel_translated_menu_link_alter(&$item) { - if ($item['link_path'] == 'fb/devel/linter' || - $item['link_path'] == FB_DEVEL_URL_LINTER) { - //dpm($item, __FUNCTION__); - $item['href'] = FB_DEVEL_URL_LINTER; // href matters. link_path and router path do not. - $item['localized_options']['query'] = array( - 'url' => url(request_path(), array('absolute' => TRUE)), - ); - } -} - -/** - * Implements hook_init(). - */ -function fb_devel_init() { - if (user_access('access devel information')) { - // Add our module's javascript. - drupal_add_js(drupal_get_path('module', 'fb_devel') . '/fb_devel.js'); - } - - // fb_settings.inc sanity check. - if (isset($GLOBALS['fb_init_no_settings'])) { - if (user_access('access administration pages') && (module_exists('fb_canvas') || modules_exists('fb_tab'))) { - // fb_settings.php must be included for canvas or tab support. - drupal_set_message(t('!drupal_for_facebook (fb_canvas.module) has been enabled, but fb_settings.inc is not included in settings.php. Please read the !readme.', - array('!drupal_for_facebook' => l(t('Drupal for Facebook'), 'http://drupal.org/project/fb'), - // This link should work with clean URLs - // disabled. - '!readme' => 'README.txt')), 'error'); - } - } - - - - // $conf['fb_apikey'] sanity check - if ($apikey = variable_get(FB_VAR_APIKEY, NULL)) { - $fb_app = fb_get_app(array('apikey' => $apikey)); - $message = t('Drupal for Facebook no longer uses the \'fb_apikey\' variable. Change $conf[\'fb_apikey\'] in your settings.php. Use $conf[\'fb_id\'] instead, and make the value the app id (%app_id) instead of the apikey.', array( - '%app_id' => $fb_app->id, - )); - if (user_access(FB_PERM_ADMINISTER)) { - drupal_set_message($message, 'error'); - } - watchdog('fb_devel', $message, array(), WATCHDOG_WARNING); - } - - // $conf['fb_id'] sanity check - if ($id = variable_get(FB_VAR_ID, NULL)) { - if ($label = variable_get(FB_CONNECT_VAR_PRIMARY, NULL)) { - $fb_app = fb_get_app(array('label' => $label)); - if ($fb_app && ($fb_app->id != $id)) { - $message = t('Drupal for Facebook has detected a problem. $conf[\'fb_id\'] (%fb_id) is not the same as the primary application %label (%fb_primary_id).', - array( - '%fb_id' => $id, - '%fb_primary_id' => $fb_app->id, - '%label' => $fb_app->label, - )); - if (user_access(FB_PERM_ADMINISTER)) { - drupal_set_message($message, 'error'); - } - watchdog('fb_devel', $message, array(), WATCHDOG_WARNING); - } - } - } - - // fb_user table sanity check - if (module_exists('fb_user')) { - if (db_query("SELECT count(*) FROM {authmap} WHERE module='fb_user'")->fetchField()) { - $message = 'fb_user data has not been migrated from authmap table. Run update.php.'; - $args = array(); - if (user_access('access administration pages')) { - drupal_set_message(t($message, $args), 'error'); - } - watchdog('fb_devel', $message, $args, WATCHDOG_ERROR); - } - } - - // Old url rewrite sanity check. - if ($id = fb_settings(FB_SETTINGS_CB)) { - if ($GLOBALS['_fb_app']->apikey == $id) { - $message = t('Facebook callback URLs have changed. They now include the app\'s ID, instead of APIKEY. Your application %label has not been updated. Either sync properties or manually change callbacks on remote settings.', array( - '%label' => $GLOBALS['_fb_app']->title, - '!sync_url' => url(FB_PATH_ADMIN_APPS . '/' . $GLOBALS['_fb_app']->label . '/fb/set_props', array( - 'fb_url_alter' => FALSE, - )), - )); - if (user_access('access administration pages')) { - drupal_set_message(t($message, $args), 'warning'); - } - watchdog('fb_devel', $message, $args, WATCHDOG_WARNING); - } - } -} - -/** - * Implements hook_fb(). - */ -function fb_devel_fb($op, $data, &$return) { - $fb_app = isset($data['fb_app']) ? $data['fb_app'] : NULL; - $fb = isset($data['fb']) ? $data['fb'] : NULL; - $errors = 0; - - if ($op == FB_OP_INITIALIZE) { - - if (fb_settings(FB_SETTINGS_APIKEY) && - ($fb_app->apikey != fb_settings(FB_SETTINGS_APIKEY))) { - $message = t('Drupal for Facebook has detected a problem. The initialized app has an apikey (%fb_app_apikey), while the settings indicates a different apikey (%fb_settings_apikey).', array( - '%fb_app_apikey' => $fb_app->apikey, - '%fb_settings_apikey' => fb_settings(FB_SETTINGS_APIKEY), - )); - drupal_set_message($message, 'error'); - watchdog('fb_devel', $message, array(), WATCHDOG_WARNING); - $errors++; - } - - // This value comes from url rewriting. Recommended on canvas pages. - $id = fb_settings(FB_SETTINGS_CB); - - // Sanity check. - if ($id && $id != $fb_app->id) { - $message = t('fb_app id (%fb_app_id) does not equal fb settings id (%fb_settings_id). This is usually caused by the wrong callback url on your facebook application settings form.', - array('%fb_app_id' => $fb_app->id, - '%fb_settings_id' => $id, - '!url' => "http://www.facebook.com/developers/apps.php", - )); - drupal_set_message($message, 'warning'); - watchdog('fb_devel', $message, array(), WATCHDOG_WARNING); - $errors++; - } - - // Catch badly formed links ASAP. - if ($id && !fb_is_canvas() && !fb_is_tab()) { - // Skip check on callbacks from facebook. - if ((arg(0) != 'fb_app') || (arg(1) != 'event')) { - $message = t('URL starts with %prefix. This should never happen on connect pages. Did the previous page have a badly formed link?', array( - '%prefix' => FB_SETTINGS_CB . '/' . $id, - )); - drupal_set_message($message, 'error'); - $errors++; - } - } - - // path replacement sanity check - global $base_path; - if ($base_path == "/{$fb_app->canvas}/") { - $message = t('Facebook canvas page matches Drupal base_path (%base_path). This is currently not supported.', - array('%base_path' => $base_path)); - drupal_set_message($message, 'error'); - watchdog('fb_devel', $message); - } - - // Old API Sanity check. - if (isset($_REQUEST['fb_sig'])) { - $message = t('Passed old-style fb_sig parameters. Go to !url, edit your application. Under "migrations" enable "new sdks".', array( - '!url' => 'http://www.facebook.com/developers/apps.php', - )); - dpm($message); - watchdog('fb_devel', $message); - } - - // server URL sanity check - // This is an expensive test, because it invokes api_client. - try { - $props = $fb->api(array( - 'method' => 'admin.getAppProperties', - 'access_token' => fb_get_token($fb), - 'properties' => array('connect_url', 'callback_url'), - )); - $props = json_decode($props, TRUE); - if (is_array($props)) { - foreach ($props as $prop => $url) { - if ($url && (strpos($url, $GLOBALS['base_url']) === FALSE)) { - $message = t('The Facebook Application labeled %label has a suspicious %prop. The value is %value, while something starting with %url was expected. Consider editing !applink.', array( - '%label' => $fb_app->label, - '%prop' => $prop, - '%value' => $url, - '%url' => $GLOBALS['base_url'], - '!applink' => l($fb_app->label, FB_PATH_ADMIN_APPS . '/' . $fb_app->label), - )); - - if (user_access('access administration pages')) { - drupal_set_message($message, 'warning'); - } - watchdog('fb_devel', $message); - } - } - } - } - catch (Exception $e) { - dpm($e, __FUNCTION__); - if ($e->getCode() == 102) { - // Session key invalid or no longer valid 102, which we can ignore. - } - else { - fb_log_exception($e, t('Failed to get app properties for %name.', array('%name' => $fb_app->title))); - } - } - - // App data sanity checks. - $fb_app_data = fb_get_app_data($fb_app); - - // Account mapping format has changed! - if (isset($fb_app_data['fb_user']) && !is_array($fb_app_data['fb_user']['map_account'])) { - $message = 'The options for mapping facebook account to local accounts has changed. You must manually edit your application. Look for the map accounts options under facebook user settings.'; - $args = array('!url' => url(FB_PATH_ADMIN_APPS . '/' . $fb_app->label)); - if (user_access('access administration pages')) { - drupal_set_message(t($message, $args), 'error'); - } - watchdog('fb_devel', $message, $args, WATCHDOG_ERROR); - } - - } - - elseif ($op == FB_APP_OP_EVENT) { - $type = $data['event_type']; - // Facebook has notified us of some event. - $message = t('Facebook has notified the %label application of a %type event.', - array('%label' => $fb_app->label, - '%type' => $type)); - $message .= '
' . print_r($data, 1);
-    $message .= print_r($_REQUEST, 1) . '
'; - watchdog('fb_devel', $message); - } - - elseif ($op == FB_OP_JS) { - // Start debugger - //$return[] = "debugger; // added by fb_devel.module"; - } - - elseif ($op == FB_OP_POST_INIT) { - if (isset($_SESSION['fb_devel'])) { - // Counter helps track how long session has been around. - $_SESSION['fb_devel']['FB_OP_POST_INIT'] = $_SESSION['fb_devel']['FB_OP_POST_INIT'] + 1; - } - else { - $_SESSION['fb_devel']['FB_OP_POST_INIT'] = 1; - } - - // Javascript to help us with sanity checks. - drupal_add_js(array( - 'fb_devel' => array( - 'session_id' => session_id(), - 'session_name' => session_name(), - ), - ), 'setting'); - } - elseif ($op == FB_OP_AJAX_EVENT) { - if (fb_verbose() == 'extreme' && FALSE) { // disabled to prevent console not defined error. - $session_id = session_id(); - $session_name = session_name(); - $return[] = "console.log('fb_devel.module extreme verbosity: original session_id is ' + Drupal.settings.fb_devel.session_name + ':' + Drupal.settings.fb_devel.session_id + ', session_id during FB_OP_AJAX_EVENT is $session_name:$session_id');"; - $fbu = fb_facebook_user($data['fb']); - $return[] = "console.log('fb_facebook_user() during FB_OP_AJAX_EVENT is $fbu, data[event_data][fbu] is {$data[event_data][fbu]}');"; - $return[] = "console.log('_COOKIE[fbu_{$fb_app->apikey}] is " . $_COOKIE['fbu_' . $fb_app->apikey] . "');"; - $return[] = "FB_JS.reload();"; - } - } -} - -/** - * Provides a page with useful debug info. - * - * @TODO - clean this up and rely less on drupal_set_message() and dpm(). - */ -function fb_devel_page() { - global $_fb, $_fb_app; - global $user; - - if (isset($_REQUEST['require_login']) && $_REQUEST['require_login']) - fb_require_authorization($_fb); - - $items = array(); - - $items['fb_settings'] = fb_settings(); - - if ($_fb) { - - $items['$GLOBALS[_fb]'] = $_fb; - - // 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())); - 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("base_url: " . $GLOBALS['base_url'])); - drupal_set_message(t("base_path: " . $GLOBALS['base_path'])); - drupal_set_message(t("url() returns: " . url())); - } - - if ($fbu = fb_get_fbu($user)) { - $path = "fb/devel/fbu/$fbu"; - drupal_set_message(t("Learn more about the current user at !link", - array('!link' => l($path, $path)))); - } - - dpm(fb_get_fbu($user), 'Facebook user via fb_get_fbu'); - //dpm($user, "Local user " . theme('username', $user)); - - if (isset($GLOBALS['fb_connect_apikey'])) { - drupal_set_message(t("fb_connect_apikey = " . $GLOBALS['fb_connect_apikey'])); - } - - dpm(fb_settings(), 'fb_settings()'); - 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) { - if (is_array($val) || is_object($val)) { - $markup = print_r($val, 1); - } - else { - $markup = $val; - } - $out[$key] = array( - '#prefix' => "
$key
",
-      '#suffix' => "
", - '#markup' => $markup, - ); - } - - return render($out); -} - - -/** - * Provides a profile tab (FBML) with useful debug info. - * - */ -function fb_devel_tab() { - global $_fb, $_fb_app; - global $user; - - $info['session_id'] = session_id(); - $info['session_name'] = session_name(); - $info['cookie domain'] = fb_settings(FB_SETTINGS_COOKIE_DOMAIN); - - // Tests for links - $link_test = url(current_path(), array('absolute' => TRUE)); - $info['link test'] = "link test (processed)"; - $info['link test 2'] = "link test (not processed)"; - - //$info['fb_app'] = $_fb_app; - //$info['fb'] = $_fb; - $info['fb_settings'] = fb_settings(); - $info['REQUEST'] = $_REQUEST; - $info['SESSION'] = $_SESSION; - $info['COOKIE'] = $_COOKIE; - - if (isset($_fb)) { - $info['fb->getSignedRequest()'] = $_fb->getSignedRequest(); - $fbu = fb_facebook_user(); - try { - $info["fb->api(/$fbu)"] = $_fb->api('/' . $fbu); - } - catch (Exception $e) { - $info["fb->api(/$fbu)"] = $e->getMessage(); - } - - if ($app_id = $_REQUEST['fb_sig_app_id']) { - try { - $info['fb->api(fb_sig_app_id)'] = $_fb->api($_REQUEST['fb_sig_app_id']); - } - catch (Exception $e) { - $info['fb->api(fb_sig_app_id)'] = $e->getMessage(); - } - } - } - - print '

fb_devel.module tab

'; - foreach ($info as $key => $value) { - print "

$key:\n"; - if (is_array($value)) { - print '

' . check_plain(print_r($value, 1)) . '
'; - } - elseif (is_object($value)) { - print '
' . check_plain(print_r($value, 1)) . '
'; - } - else { - print '
' . $value . '
'; - } - print "\n

\n\n"; - } - - exit(); -} - -/** - * A page which tests function which work with facebook user ids - */ -function fb_devel_fbu_page($fbu = NULL) { - global $_fb, $_fb_app; - if ($fbu) { - $info = $_fb->api($fbu, array('metadata' => 1)); - $output = "

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

\n"; - $output .= ""; - $output .= "
" . print_r($info, 1) . "
"; - - foreach (array('statuses') as $connection) { - try { - if ($data = $_fb->api($fbu . '/' . $connection)) { - $output .= "

$connection

";
-          $output .= print_r($data, 1);
-          $output .= "

\n\n"; - } - } - catch (FacebookApiException $e) { - fb_log_exception($e, t('Failed to lookup %connection', array('%connection' => $fbu . '/' . $connection))); - } - } - try { - $friends = $_fb->api($fbu . '/friends'); - //dpm($friends, "$fbu/friends returned"); - $items = array(); - foreach ($friends['data'] as $data) { - $items[] = l($data['name'], "fb/devel/fbu/{$data[id]}"); - } - if (count($items)) { - $output .= "\n

Known friends:

  • "; - $output .= implode("
  • \n
  • ", $items); - $output .= "

\n\n"; - } - } - catch (FacebookApiException $e) { - fb_log_exception($e, t('Failed to lookup friends of facebook user %fbu', array('%fbu' => $fbu))); - } - - if (module_exists('fb_user')) { - $local_friends = fb_user_get_local_friends($fbu); - - $items = array(); - foreach ($local_friends as $uid) { - $account = user_load(array('uid' => $uid)); - $items[] = theme('username', $account); - } - if (count($items)) { - $output .= "\n

Local friends:

  • "; - $output .= implode("
  • \n
  • ", $items); - $output .= "

\n\n"; - } - } - } - else - drupal_set_message(t("You have to specify a facebook user id."), 'error'); - return $output; -} - - -/** - * Provide some information for testing AHAH and AJAX scenarios. - */ -function fb_devel_js() { - $data = '
';
-
-  $data .= "session_name() = " . session_name() . "\n";
-  $data .= "session_id() = " . session_id() . "\n";
-  $data .= "REQUEST: " . dprint_r($_REQUEST, 1) . "\n";
-  $data .= "SESSION: " . dprint_r($_SESSION, 1) . "\n";
-  $data .= '
'; - - print drupal_json_output(array('status' => TRUE, 'data' => $data)); - exit(); -} - -function fb_devel_block_info() { - // Provide two copies of same block, for iframe scenarios. - $items[0]['info'] = t('Facebook Devel Page Info'); - $items[1]['info'] = t('Facebook Devel Page Info 2'); - return $items; -} - -function fb_devel_block_view($delta = '') { - if (user_access('access devel information')) { - return array( - 'subject' => t('Facebook Devel Info'), - 'content' => drupal_get_form('fb_devel_info'), - ); - } -} - -function fb_devel_info() { - global $_fb, $_fb_app; - global $user; - global $base_url; - global $base_path; - - $info = array(); - - if ($_fb) { - if (fb_settings(FB_SETTINGS_TYPE) == FB_SETTINGS_TYPE_CANVAS) { - $info['Page Status'] = t('Serving canvas page.'); - } - elseif (fb_settings(FB_SETTINGS_TYPE) == FB_SETTINGS_TYPE_CONNECT) { - $info['Page Status'] = t('Connected via Facebook Connect'); - } - elseif (fb_settings(FB_SETTINGS_TYPE) == FB_SETTINGS_TYPE_PROFILE) { - $info['Page Status'] = t('Serving deprecated FBML profile tab'); - } - elseif (fb_settings(FB_SETTINGS_TYPE) == FB_SETTINGS_TYPE_PAGE_TAB) { - $info['Page Status'] = t('Iframe tab on page %page', array( - '%page' => fb_settings(FB_SETTINGS_PAGE_ID), - )); - } - elseif (!fb_settings(FB_SETTINGS_TYPE)) { - $info['Page Status'] = t('Facebook PHP SDK initialized.'); // Either a facebook connect page, or canvas page where user has not authorized app. - } - else { - $info['Page Status'] = t('Global fb instance is set, but page type unknown (%type).', - array('%type' => fb_settings(FB_SETTINGS_TYPE), - )); - } - $fbu = fb_facebook_user(); - $info['fb_facebook_user'] = $fbu; - - if (!$fbu) { - $info['login button'] = theme('fb_login_button', array('text' => 'Connect')); - $info['login link'] = "fb login"; - } - - // Tests for link on canvas pages (iframe cookie test) - //$link_test = url(fb_scrub_urls($_REQUEST['q']), array('absolute' => TRUE)); - //$info['link test'] = "link test (processed)"; - //$info['link test 2'] = "link test (not processed)"; - - if ($fbu && FALSE) { // Disabled as this expensive check can cause problems. - if (!fb_api_check_session($_fb)) { - $info['fb_api_check_session()'] = t('Returned FALSE'); - } - else { - $info['fb_api_check_session()'] = t('Returned TRUE'); - } - } - } - else { - $info['Page Status'] = t('Not a canvas page.'); - } - // Use theme_username() rather than theme('username') because we want link to local user, even when FBML is enabled - if ($user->uid) { - $name = $user->name; - } - else { - $name = "anonymous"; - } - $info['local user'] = theme_username(array('account' => $user, - 'attributes_array' => array(), - 'name' => $name, - 'extra' => '')); - $info['fb_get_fbu'] = fb_get_fbu($user->uid); - $info['base_url'] = $base_url; - $info['base_path'] = $base_path; - $info['url() returns'] = url(); - $info['current_path'] = current_path(); - $info['arg(0) is'] = arg(0); - $info['arg(1) is'] = arg(1); - $info['session_id'] = session_id(); - $info['session_name'] = session_name(); - $info['fbsettings(FB_SETTINGS_COOKIE_DOMAIN)'] = fb_settings(FB_SETTINGS_COOKIE_DOMAIN); - - $info['request'] = $_REQUEST; - $info['user'] = $GLOBALS['user']; - $info['fb_app'] = $_fb_app; - $info['session'] = $_SESSION; - $info['cookies'] = $_COOKIE; - - - if ($_fb) { - $info['signed_request'] = $_fb->getSignedRequest(); - try { - // app properties - $token = fb_get_token($_fb); - $props = $_fb->api(array( - 'method' => 'admin.getAppProperties', - 'properties' => 'about_url', 'application_name', - 'access_token' => $token, - )); - $info['application properties'] = $props; - - } - catch (FacebookApiException $e) { - fb_log_exception($e, "failed to get app properties"); - } - $info["fb->getSession()"] = $_fb->getSession(); - $info["fb->getSignedRequest()"] = $_fb->getSignedRequest(); - if ($fbu) { - try { - $info["fb->api($fbu)"] = $_fb->api($fbu, array( - 'access_token' => fb_get_token($_fb), - )); - } - catch (FacebookApiException $e) { - fb_log_exception($e, "failed to look up _fb->api($fbu)"); - } - } - } - - $form = array(); - foreach ($info as $key => $val) { - if (is_string($val) || is_numeric($val) || !$val) { - $form[$key] = array('#markup' => t($key) . ' = ' . $val, - '#weight' => count($form), - '#suffix' => '
', - ); - - } - else { - $form[] = array( - '#type' => 'fieldset', - '#title' => t($key), - '#collapsible' => TRUE, - '#collapsed' => TRUE, - '#weight' => count($form), - 'value' => array( - '#prefix' => '', - '#suffix' => '', - '#type' => 'markup', - '#markup' => dprint_r($val, 1)), - ); - } - } - return $form; -} - -/** - * Implements hook_user_view(). - */ -function fb_devel_user_view($account, $view_mode, $langcod) { - if (user_access('administer users') && user_access('access devel information')) { - $account->content['fb_devel'] = array( - '#type' => 'fieldset', - '#title' => t('Drupal for Facebook Devel'), - '#description' => t('Information from facebook API, visible only to administrators.'), - '#collapsible' => TRUE, - '#collapsed' => TRUE, - '#weight' => 99, - ); - - foreach (fb_get_all_apps() as $fb_app) { - $account->content['fb_devel'][$fb_app->label] = array( - '#type' => 'fieldset', - '#title' => $fb_app->title, - ); - if ($fbu = fb_get_fbu($account, $fb_app)) { - $fb = fb_api_init($fb_app); - try { - // Don't use fb_api() because we don't want the caching here. - $info = $fb->api("/$fbu", array('access_token' => fb_get_token($fb))); - - //$info = fb_users_getInfo(array($fbu), $fb, TRUE); - $account->content['fb_devel'][$fb_app->label]['info'] = array( - '#type' => 'markup', - '#markup' => "facebook graph for /$fbu:" . dprint_r($info, 1), - ); - - if ($token = fb_get_token($fb, $fbu)) { - $account->content['fb_devel'][$fb_app->label]['token'] = array( - '#type' => 'markup', - '#markup' => 'access_token: ' . fb_get_token($fb, $fbu), - ); - } - } - catch (Exception $e) { - $account->content['fb_devel'][$fb_app->label]['#description'] = $e->getMessage(); - $account->content['fb_devel'][$fb_app->label]['info'] = array( - '#type' => 'fieldset', - '#title' => t('Failed to get info for !app.', - array('!app' => $fb_app->title)), - '#collapsible' => TRUE, - '#collapsed' => TRUE, - 'exception' => array( - '#type' => 'markup', - '#markup' => dprint_r($e, 1), - ), - ); - } - } - else { - $account->content['fb_devel'][$fb_app->label]['info'] = array( - '#type' => 'markup', - '#markup' => t('No mapping to a facebook account.'), - ); - } - } - } -} - -/** - * Implements hook_cron(). - * - * Remove obsolete data from {users} table. Not a serious problem, - * just cruft in the database which should never have been saved. - * Clean it up. - */ -function fb_devel_cron() { - $limit = 10; - $result = db_query_range('SELECT * FROM {users} WHERE data LIKE :as OR data like :iau OR data like :fbu', - 0, $limit, array(':as' => '%\"app_specific\"%', - ':iau' => '%\"is_app_user\"%', - ':fbu' => '%\"fbu\"%')); - - foreach ($result as $account) { - $data = unserialize($account->data); - // Clean out the bogus data. - foreach (array('app_specific', 'username', 'fbu', 'info') as $key) { - unset($data[$key]); - } - db_update('users') - ->fields(array('data' => serialize($data), 'uid' => $account->uid)) - ->execute; - - /* - db_query("UPDATE {users} SET data=:data WHERE uid=:uid", - serialize($data), $account->uid); - */ - if (fb_verbose()) { - print(t('Cleaned up data for user %name (%uid), it is now: !data', - array('%name' => $account->name, - '%uid' => $account->uid, - '!data' => '
' . print_r($data, 1) . '
', - ))); - } - } -} - - -function fb_devel_fb_tab($op, $data, &$return) { - // debug. - if (fb_verbose() === 'extreme') { - $return['fb_devel'] = array( - '#type' => 'markup', - '#markup' => __FUNCTION__ . ':' . print_r($data, 1), - '#prefix' => '
',
-      '#suffix' => '
', - '#weight' => 99, - ); - if ($data['profile_id'] && isset($data['fb'])) { - $return['fb_devel']['profile_id'] = array( - '#type' => 'markup', - '#markup' => __FUNCTION__ . ':' . print_r($data['fb']->api($data['profile_id']), 1), - '#prefix' => '
',
-        '#suffix' => '
', - '#weight' => 99, - ); - } - } -} - - -/** - * Wrapper for fb_fql_query(). Useful for debugging. - */ -function fb_devel_fql_query($fb, $query, $params = array()) { - if (!is_object($fb)) { - dpm(debug_backtrace(), __FUNCTION__); - return; - } - $url_params = $params; - $url_params['query'] = $query; - $url_params['format'] = 'JSON'; - $url = url("https://api.facebook.com/method/fql.query", array('query' => $url_params)); - dpm(func_get_args(), __FUNCTION__); - drupal_set_message(l($url, $url)); - return fb_fql_query($fb, $query, $params); -} + 'fb_devel_page', + 'type' => MENU_CALLBACK, + 'access callback' => TRUE, /* TODO: restrict access */ + ); + + $items['fb/devel/fbu'] = array( + 'page callback' => 'fb_devel_fbu_page', + 'type' => MENU_CALLBACK, + 'access callback' => TRUE, + ); + + $items['fb/devel/tab'] = array( + 'page callback' => 'fb_devel_tab', + 'type' => MENU_CALLBACK, + 'access callback' => TRUE, /* TODO: restrict access */ + ); + + // Return some info for debugging AHAH problems + $items['fb/devel/js'] = array( + 'page callback' => 'fb_devel_js', + 'type' => MENU_CALLBACK, + 'access callback' => TRUE, + ); + + $items['fb/devel/linter'] = array( + 'title' => 'Facebook linter', + 'page callback' => 'drupal_not_found', // Because we alter link, below. + 'type' => MENU_NORMAL_ITEM, + 'access arguments' => array('access devel information'), + 'menu_name' => 'devel', + 'options' => array('alter' => TRUE), + ); + + return $items; +} + +// Trick learned from devel.module, to make link to linter for the current page. +function fb_devel_translated_menu_link_alter(&$item) { + if ($item['link_path'] == 'fb/devel/linter' || + $item['link_path'] == FB_DEVEL_URL_LINTER) { + //dpm($item, __FUNCTION__); + $item['href'] = FB_DEVEL_URL_LINTER; // href matters. link_path and router path do not. + $item['localized_options']['query'] = array( + 'url' => url(request_path(), array('absolute' => TRUE)), + ); + } +} + +/** + * Implements hook_init(). + */ +function fb_devel_init() { + if (user_access('access devel information')) { + // Add our module's javascript. + drupal_add_js(drupal_get_path('module', 'fb_devel') . '/fb_devel.js'); + } + + // fb_settings.inc sanity check. + if (isset($GLOBALS['fb_init_no_settings'])) { + if (user_access('access administration pages') && (module_exists('fb_canvas') || module_exists('fb_tab'))) { + // fb_settings.php must be included for canvas or tab support. + drupal_set_message(t('!drupal_for_facebook (fb_canvas.module) has been enabled, but fb_settings.inc is not included in settings.php. Please read the !readme.', + array('!drupal_for_facebook' => l(t('Drupal for Facebook'), 'http://drupal.org/project/fb'), + // This link should work with clean URLs + // disabled. + '!readme' => 'README.txt')), 'error'); + } + } + + + + // $conf['fb_apikey'] sanity check + if ($apikey = variable_get(FB_VAR_APIKEY, NULL)) { + $fb_app = fb_get_app(array('apikey' => $apikey)); + $message = t('Drupal for Facebook no longer uses the \'fb_apikey\' variable. Change $conf[\'fb_apikey\'] in your settings.php. Use $conf[\'fb_id\'] instead, and make the value the app id (%app_id) instead of the apikey.', array( + '%app_id' => $fb_app->id, + )); + if (user_access(FB_PERM_ADMINISTER)) { + drupal_set_message($message, 'error'); + } + watchdog('fb_devel', $message, array(), WATCHDOG_WARNING); + } + + // $conf['fb_id'] sanity check + if ($id = variable_get(FB_VAR_ID, NULL)) { + if ($label = variable_get(FB_CONNECT_VAR_PRIMARY, NULL)) { + $fb_app = fb_get_app(array('label' => $label)); + if ($fb_app && ($fb_app->id != $id)) { + $message = t('Drupal for Facebook has detected a problem. $conf[\'fb_id\'] (%fb_id) is not the same as the primary application %label (%fb_primary_id).', + array( + '%fb_id' => $id, + '%fb_primary_id' => $fb_app->id, + '%label' => $fb_app->label, + )); + if (user_access(FB_PERM_ADMINISTER)) { + drupal_set_message($message, 'error'); + } + watchdog('fb_devel', $message, array(), WATCHDOG_WARNING); + } + } + } + + // fb_user table sanity check + if (module_exists('fb_user')) { + if (db_query("SELECT count(*) FROM {authmap} WHERE module='fb_user'")->fetchField()) { + $message = 'fb_user data has not been migrated from authmap table. Run update.php.'; + $args = array(); + if (user_access('access administration pages')) { + drupal_set_message(t($message, $args), 'error'); + } + watchdog('fb_devel', $message, $args, WATCHDOG_ERROR); + } + } + + // Old url rewrite sanity check. + if ($id = fb_settings(FB_SETTINGS_CB)) { + if ($GLOBALS['_fb_app']->apikey == $id) { + $message = t('Facebook callback URLs have changed. They now include the app\'s ID, instead of APIKEY. Your application %label has not been updated. Either sync properties or manually change callbacks on remote settings.', array( + '%label' => $GLOBALS['_fb_app']->title, + '!sync_url' => url(FB_PATH_ADMIN_APPS . '/' . $GLOBALS['_fb_app']->label . '/fb/set_props', array( + 'fb_url_alter' => FALSE, + )), + )); + if (user_access('access administration pages')) { + drupal_set_message(t($message, $args), 'warning'); + } + watchdog('fb_devel', $message, $args, WATCHDOG_WARNING); + } + } +} + +/** + * Implements hook_fb(). + */ +function fb_devel_fb($op, $data, &$return) { + $fb_app = isset($data['fb_app']) ? $data['fb_app'] : NULL; + $fb = isset($data['fb']) ? $data['fb'] : NULL; + $errors = 0; + + if ($op == FB_OP_INITIALIZE) { + + if (fb_settings(FB_SETTINGS_APIKEY) && + ($fb_app->apikey != fb_settings(FB_SETTINGS_APIKEY))) { + $message = t('Drupal for Facebook has detected a problem. The initialized app has an apikey (%fb_app_apikey), while the settings indicates a different apikey (%fb_settings_apikey).', array( + '%fb_app_apikey' => $fb_app->apikey, + '%fb_settings_apikey' => fb_settings(FB_SETTINGS_APIKEY), + )); + drupal_set_message($message, 'error'); + watchdog('fb_devel', $message, array(), WATCHDOG_WARNING); + $errors++; + } + + // This value comes from url rewriting. Recommended on canvas pages. + $id = fb_settings(FB_SETTINGS_CB); + + // Sanity check. + if ($id && $id != $fb_app->id) { + $message = t('fb_app id (%fb_app_id) does not equal fb settings id (%fb_settings_id). This is usually caused by the wrong callback url on your facebook application settings form.', + array('%fb_app_id' => $fb_app->id, + '%fb_settings_id' => $id, + '!url' => "http://www.facebook.com/developers/apps.php", + )); + drupal_set_message($message, 'warning'); + watchdog('fb_devel', $message, array(), WATCHDOG_WARNING); + $errors++; + } + + // Catch badly formed links ASAP. + if ($id && !fb_is_canvas() && !fb_is_tab()) { + // Skip check on callbacks from facebook. + if ((arg(0) != 'fb_app') || (arg(1) != 'event')) { + $message = t('URL starts with %prefix. This should never happen on connect pages. Did the previous page have a badly formed link?', array( + '%prefix' => FB_SETTINGS_CB . '/' . $id, + )); + drupal_set_message($message, 'error'); + $errors++; + } + } + + // path replacement sanity check + global $base_path; + if ($base_path == "/{$fb_app->canvas}/") { + $message = t('Facebook canvas page matches Drupal base_path (%base_path). This is currently not supported.', + array('%base_path' => $base_path)); + drupal_set_message($message, 'error'); + watchdog('fb_devel', $message); + } + + // Old API Sanity check. + if (isset($_REQUEST['fb_sig'])) { + $message = t('Passed old-style fb_sig parameters. Go to !url, edit your application. Under "migrations" enable "new sdks".', array( + '!url' => 'http://www.facebook.com/developers/apps.php', + )); + dpm($message); + watchdog('fb_devel', $message); + } + + // server URL sanity check + // This is an expensive test, because it invokes api_client. + try { + $props = $fb->api(array( + 'method' => 'admin.getAppProperties', + 'access_token' => fb_get_token($fb), + 'properties' => array('connect_url', 'callback_url'), + )); + $props = json_decode($props, TRUE); + if (is_array($props)) { + foreach ($props as $prop => $url) { + if ($url && (strpos($url, $GLOBALS['base_url']) === FALSE)) { + $message = t('The Facebook Application labeled %label has a suspicious %prop. The value is %value, while something starting with %url was expected. Consider editing !applink.', array( + '%label' => $fb_app->label, + '%prop' => $prop, + '%value' => $url, + '%url' => $GLOBALS['base_url'], + '!applink' => l($fb_app->label, FB_PATH_ADMIN_APPS . '/' . $fb_app->label), + )); + + if (user_access('access administration pages')) { + drupal_set_message($message, 'warning'); + } + watchdog('fb_devel', $message); + } + } + } + } + catch (Exception $e) { + dpm($e, __FUNCTION__); + if ($e->getCode() == 102) { + // Session key invalid or no longer valid 102, which we can ignore. + } + else { + fb_log_exception($e, t('Failed to get app properties for %name.', array('%name' => $fb_app->title))); + } + } + + // App data sanity checks. + $fb_app_data = fb_get_app_data($fb_app); + + // Account mapping format has changed! + if (isset($fb_app_data['fb_user']) && !is_array($fb_app_data['fb_user']['map_account'])) { + $message = 'The options for mapping facebook account to local accounts has changed. You must manually edit your application. Look for the map accounts options under facebook user settings.'; + $args = array('!url' => url(FB_PATH_ADMIN_APPS . '/' . $fb_app->label)); + if (user_access('access administration pages')) { + drupal_set_message(t($message, $args), 'error'); + } + watchdog('fb_devel', $message, $args, WATCHDOG_ERROR); + } + + } + + elseif ($op == FB_APP_OP_EVENT) { + $type = $data['event_type']; + // Facebook has notified us of some event. + $message = t('Facebook has notified the %label application of a %type event.', + array('%label' => $fb_app->label, + '%type' => $type)); + $message .= '
' . print_r($data, 1);
+    $message .= print_r($_REQUEST, 1) . '
'; + watchdog('fb_devel', $message); + } + + elseif ($op == FB_OP_JS) { + // Start debugger + //$return[] = "debugger; // added by fb_devel.module"; + } + + elseif ($op == FB_OP_POST_INIT) { + if (isset($_SESSION['fb_devel'])) { + // Counter helps track how long session has been around. + $_SESSION['fb_devel']['FB_OP_POST_INIT'] = $_SESSION['fb_devel']['FB_OP_POST_INIT'] + 1; + } + else { + $_SESSION['fb_devel']['FB_OP_POST_INIT'] = 1; + } + + // Javascript to help us with sanity checks. + drupal_add_js(array( + 'fb_devel' => array( + 'session_id' => session_id(), + 'session_name' => session_name(), + ), + ), 'setting'); + } + elseif ($op == FB_OP_AJAX_EVENT) { + if (fb_verbose() == 'extreme' && FALSE) { // disabled to prevent console not defined error. + $session_id = session_id(); + $session_name = session_name(); + $return[] = "console.log('fb_devel.module extreme verbosity: original session_id is ' + Drupal.settings.fb_devel.session_name + ':' + Drupal.settings.fb_devel.session_id + ', session_id during FB_OP_AJAX_EVENT is $session_name:$session_id');"; + $fbu = fb_facebook_user($data['fb']); + $return[] = "console.log('fb_facebook_user() during FB_OP_AJAX_EVENT is $fbu, data[event_data][fbu] is {$data[event_data][fbu]}');"; + $return[] = "console.log('_COOKIE[fbu_{$fb_app->apikey}] is " . $_COOKIE['fbu_' . $fb_app->apikey] . "');"; + $return[] = "FB_JS.reload();"; + } + } +} + +/** + * Provides a page with useful debug info. + * + * @TODO - clean this up and rely less on drupal_set_message() and dpm(). + */ +function fb_devel_page() { + global $_fb, $_fb_app; + global $user; + + if (isset($_REQUEST['require_login']) && $_REQUEST['require_login']) + fb_require_authorization($_fb); + + $items = array(); + + $items['fb_settings'] = fb_settings(); + + if ($_fb) { + + $items['$GLOBALS[_fb]'] = $_fb; + + // 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())); + 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("base_url: " . $GLOBALS['base_url'])); + drupal_set_message(t("base_path: " . $GLOBALS['base_path'])); + drupal_set_message(t("url() returns: " . url())); + } + + if ($fbu = fb_get_fbu($user)) { + $path = "fb/devel/fbu/$fbu"; + drupal_set_message(t("Learn more about the current user at !link", + array('!link' => l($path, $path)))); + } + + dpm(fb_get_fbu($user), 'Facebook user via fb_get_fbu'); + //dpm($user, "Local user " . theme('username', $user)); + + if (isset($GLOBALS['fb_connect_apikey'])) { + drupal_set_message(t("fb_connect_apikey = " . $GLOBALS['fb_connect_apikey'])); + } + + dpm(fb_settings(), 'fb_settings()'); + 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) { + if (is_array($val) || is_object($val)) { + $markup = print_r($val, 1); + } + else { + $markup = $val; + } + $out[$key] = array( + '#prefix' => "
$key
",
+      '#suffix' => "
", + '#markup' => $markup, + ); + } + + return render($out); +} + + +/** + * Provides a profile tab (FBML) with useful debug info. + * + */ +function fb_devel_tab() { + global $_fb, $_fb_app; + global $user; + + $info['session_id'] = session_id(); + $info['session_name'] = session_name(); + $info['cookie domain'] = fb_settings(FB_SETTINGS_COOKIE_DOMAIN); + + // Tests for links + $link_test = url(current_path(), array('absolute' => TRUE)); + $info['link test'] = "link test (processed)"; + $info['link test 2'] = "link test (not processed)"; + + //$info['fb_app'] = $_fb_app; + //$info['fb'] = $_fb; + $info['fb_settings'] = fb_settings(); + $info['REQUEST'] = $_REQUEST; + $info['SESSION'] = $_SESSION; + $info['COOKIE'] = $_COOKIE; + + if (isset($_fb)) { + $info['fb->getSignedRequest()'] = $_fb->getSignedRequest(); + $fbu = fb_facebook_user(); + try { + $info["fb->api(/$fbu)"] = $_fb->api('/' . $fbu); + } + catch (Exception $e) { + $info["fb->api(/$fbu)"] = $e->getMessage(); + } + + if ($app_id = $_REQUEST['fb_sig_app_id']) { + try { + $info['fb->api(fb_sig_app_id)'] = $_fb->api($_REQUEST['fb_sig_app_id']); + } + catch (Exception $e) { + $info['fb->api(fb_sig_app_id)'] = $e->getMessage(); + } + } + } + + print '

fb_devel.module tab

'; + foreach ($info as $key => $value) { + print "

$key:\n"; + if (is_array($value)) { + print '

' . check_plain(print_r($value, 1)) . '
'; + } + elseif (is_object($value)) { + print '
' . check_plain(print_r($value, 1)) . '
'; + } + else { + print '
' . $value . '
'; + } + print "\n

\n\n"; + } + + exit(); +} + +/** + * A page which tests function which work with facebook user ids + */ +function fb_devel_fbu_page($fbu = NULL) { + global $_fb, $_fb_app; + if ($fbu) { + $info = $_fb->api($fbu, array('metadata' => 1)); + $output = "

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

\n"; + $output .= ""; + $output .= "
" . print_r($info, 1) . "
"; + + foreach (array('statuses') as $connection) { + try { + if ($data = $_fb->api($fbu . '/' . $connection)) { + $output .= "

$connection

";
+          $output .= print_r($data, 1);
+          $output .= "

\n\n"; + } + } + catch (FacebookApiException $e) { + fb_log_exception($e, t('Failed to lookup %connection', array('%connection' => $fbu . '/' . $connection))); + } + } + try { + $friends = $_fb->api($fbu . '/friends'); + //dpm($friends, "$fbu/friends returned"); + $items = array(); + foreach ($friends['data'] as $data) { + $items[] = l($data['name'], "fb/devel/fbu/{$data[id]}"); + } + if (count($items)) { + $output .= "\n

Known friends:

  • "; + $output .= implode("
  • \n
  • ", $items); + $output .= "

\n\n"; + } + } + catch (FacebookApiException $e) { + fb_log_exception($e, t('Failed to lookup friends of facebook user %fbu', array('%fbu' => $fbu))); + } + + if (module_exists('fb_user')) { + $local_friends = fb_user_get_local_friends($fbu); + + $items = array(); + foreach ($local_friends as $uid) { + $account = user_load(array('uid' => $uid)); + $items[] = theme('username', $account); + } + if (count($items)) { + $output .= "\n

Local friends:

  • "; + $output .= implode("
  • \n
  • ", $items); + $output .= "

\n\n"; + } + } + } + else + drupal_set_message(t("You have to specify a facebook user id."), 'error'); + return $output; +} + + +/** + * Provide some information for testing AHAH and AJAX scenarios. + */ +function fb_devel_js() { + $data = '
';
+
+  $data .= "session_name() = " . session_name() . "\n";
+  $data .= "session_id() = " . session_id() . "\n";
+  $data .= "REQUEST: " . dprint_r($_REQUEST, 1) . "\n";
+  $data .= "SESSION: " . dprint_r($_SESSION, 1) . "\n";
+  $data .= '
'; + + print drupal_json_output(array('status' => TRUE, 'data' => $data)); + exit(); +} + +function fb_devel_block_info() { + // Provide two copies of same block, for iframe scenarios. + $items[0]['info'] = t('Facebook Devel Page Info'); + $items[1]['info'] = t('Facebook Devel Page Info 2'); + return $items; +} + +function fb_devel_block_view($delta = '') { + if (user_access('access devel information')) { + return array( + 'subject' => t('Facebook Devel Info'), + 'content' => drupal_get_form('fb_devel_info'), + ); + } +} + +function fb_devel_info() { + global $_fb, $_fb_app; + global $user; + global $base_url; + global $base_path; + + $info = array(); + + if ($_fb) { + if (fb_settings(FB_SETTINGS_TYPE) == FB_SETTINGS_TYPE_CANVAS) { + $info['Page Status'] = t('Serving canvas page.'); + } + elseif (fb_settings(FB_SETTINGS_TYPE) == FB_SETTINGS_TYPE_CONNECT) { + $info['Page Status'] = t('Connected via Facebook Connect'); + } + elseif (fb_settings(FB_SETTINGS_TYPE) == FB_SETTINGS_TYPE_PROFILE) { + $info['Page Status'] = t('Serving deprecated FBML profile tab'); + } + elseif (fb_settings(FB_SETTINGS_TYPE) == FB_SETTINGS_TYPE_PAGE_TAB) { + $info['Page Status'] = t('Iframe tab on page %page', array( + '%page' => fb_settings(FB_SETTINGS_PAGE_ID), + )); + } + elseif (!fb_settings(FB_SETTINGS_TYPE)) { + $info['Page Status'] = t('Facebook PHP SDK initialized.'); // Either a facebook connect page, or canvas page where user has not authorized app. + } + else { + $info['Page Status'] = t('Global fb instance is set, but page type unknown (%type).', + array('%type' => fb_settings(FB_SETTINGS_TYPE), + )); + } + $fbu = fb_facebook_user(); + $info['fb_facebook_user'] = $fbu; + + if (!$fbu) { + $info['login button'] = theme('fb_login_button', array('text' => 'Connect')); + $info['login link'] = "fb login"; + } + + // Tests for link on canvas pages (iframe cookie test) + //$link_test = url(fb_scrub_urls($_REQUEST['q']), array('absolute' => TRUE)); + //$info['link test'] = "link test (processed)"; + //$info['link test 2'] = "link test (not processed)"; + + if ($fbu && FALSE) { // Disabled as this expensive check can cause problems. + if (!fb_api_check_session($_fb)) { + $info['fb_api_check_session()'] = t('Returned FALSE'); + } + else { + $info['fb_api_check_session()'] = t('Returned TRUE'); + } + } + } + else { + $info['Page Status'] = t('Not a canvas page.'); + } + // Use theme_username() rather than theme('username') because we want link to local user, even when FBML is enabled + if ($user->uid) { + $name = $user->name; + } + else { + $name = "anonymous"; + } + $info['local user'] = theme_username(array('account' => $user, + 'attributes_array' => array(), + 'name' => $name, + 'extra' => '')); + $info['fb_get_fbu'] = fb_get_fbu($user->uid); + $info['base_url'] = $base_url; + $info['base_path'] = $base_path; + $info['url() returns'] = url(); + $info['current_path'] = current_path(); + $info['arg(0) is'] = arg(0); + $info['arg(1) is'] = arg(1); + $info['session_id'] = session_id(); + $info['session_name'] = session_name(); + $info['fbsettings(FB_SETTINGS_COOKIE_DOMAIN)'] = fb_settings(FB_SETTINGS_COOKIE_DOMAIN); + + $info['request'] = $_REQUEST; + $info['user'] = $GLOBALS['user']; + $info['fb_app'] = $_fb_app; + $info['session'] = $_SESSION; + $info['cookies'] = $_COOKIE; + + + if ($_fb) { + $info['signed_request'] = $_fb->getSignedRequest(); + try { + // app properties + $token = fb_get_token($_fb); + $props = $_fb->api(array( + 'method' => 'admin.getAppProperties', + 'properties' => 'about_url', 'application_name', + 'access_token' => $token, + )); + $info['application properties'] = $props; + + } + catch (FacebookApiException $e) { + fb_log_exception($e, "failed to get app properties"); + } + $info["fb->getUser()"] = $_fb->getUser(); + $info["fb->getSignedRequest()"] = $_fb->getSignedRequest(); + if ($fbu) { + try { + $info["fb->api($fbu)"] = $_fb->api($fbu, array( + 'access_token' => fb_get_token($_fb), + )); + } + catch (FacebookApiException $e) { + fb_log_exception($e, "failed to look up _fb->api($fbu)"); + } + } + } + + $form = array(); + foreach ($info as $key => $val) { + if (is_string($val) || is_numeric($val) || !$val) { + $form[$key] = array('#markup' => t($key) . ' = ' . $val, + '#weight' => count($form), + '#suffix' => '
', + ); + + } + else { + $form[] = array( + '#type' => 'fieldset', + '#title' => t($key), + '#collapsible' => TRUE, + '#collapsed' => TRUE, + '#weight' => count($form), + 'value' => array( + '#prefix' => '', + '#suffix' => '', + '#type' => 'markup', + '#markup' => dprint_r($val, 1)), + ); + } + } + return $form; +} + +/** + * Implements hook_user_view(). + */ +function fb_devel_user_view($account, $view_mode, $langcod) { + if (user_access('administer users') && user_access('access devel information')) { + $account->content['fb_devel'] = array( + '#type' => 'fieldset', + '#title' => t('Drupal for Facebook Devel'), + '#description' => t('Information from facebook API, visible only to administrators.'), + '#collapsible' => TRUE, + '#collapsed' => TRUE, + '#weight' => 99, + ); + + foreach (fb_get_all_apps() as $fb_app) { + $account->content['fb_devel'][$fb_app->label] = array( + '#type' => 'fieldset', + '#title' => $fb_app->title, + ); + if ($fbu = fb_get_fbu($account, $fb_app)) { + $fb = fb_api_init($fb_app); + try { + // Don't use fb_api() because we don't want the caching here. + $info = $fb->api("/$fbu", array('access_token' => fb_get_token($fb))); + + //$info = fb_users_getInfo(array($fbu), $fb, TRUE); + $account->content['fb_devel'][$fb_app->label]['info'] = array( + '#type' => 'markup', + '#markup' => "facebook graph for /$fbu:" . dprint_r($info, 1), + ); + + if ($token = fb_get_token($fb, $fbu)) { + $account->content['fb_devel'][$fb_app->label]['token'] = array( + '#type' => 'markup', + '#markup' => 'access_token: ' . fb_get_token($fb, $fbu), + ); + } + } + catch (Exception $e) { + $account->content['fb_devel'][$fb_app->label]['#description'] = $e->getMessage(); + $account->content['fb_devel'][$fb_app->label]['info'] = array( + '#type' => 'fieldset', + '#title' => t('Failed to get info for !app.', + array('!app' => $fb_app->title)), + '#collapsible' => TRUE, + '#collapsed' => TRUE, + 'exception' => array( + '#type' => 'markup', + '#markup' => dprint_r($e, 1), + ), + ); + } + } + else { + $account->content['fb_devel'][$fb_app->label]['info'] = array( + '#type' => 'markup', + '#markup' => t('No mapping to a facebook account.'), + ); + } + } + } +} + +/** + * Implements hook_cron(). + * + * Remove obsolete data from {users} table. Not a serious problem, + * just cruft in the database which should never have been saved. + * Clean it up. + */ +function fb_devel_cron() { + $limit = 10; + $result = db_query_range('SELECT * FROM {users} WHERE data LIKE :as OR data like :iau OR data like :fbu', + 0, $limit, array(':as' => '%\"app_specific\"%', + ':iau' => '%\"is_app_user\"%', + ':fbu' => '%\"fbu\"%')); + + foreach ($result as $account) { + $data = unserialize($account->data); + // Clean out the bogus data. + foreach (array('app_specific', 'username', 'fbu', 'info') as $key) { + unset($data[$key]); + } + db_update('users') + ->fields(array('data' => serialize($data), 'uid' => $account->uid)) + ->execute; + + /* + db_query("UPDATE {users} SET data=:data WHERE uid=:uid", + serialize($data), $account->uid); + */ + if (fb_verbose()) { + print(t('Cleaned up data for user %name (%uid), it is now: !data', + array('%name' => $account->name, + '%uid' => $account->uid, + '!data' => '
' . print_r($data, 1) . '
', + ))); + } + } +} + + +function fb_devel_fb_tab($op, $data, &$return) { + // debug. + if (fb_verbose() === 'extreme') { + $return['fb_devel'] = array( + '#type' => 'markup', + '#markup' => __FUNCTION__ . ':' . print_r($data, 1), + '#prefix' => '
',
+      '#suffix' => '
', + '#weight' => 99, + ); + if ($data['profile_id'] && isset($data['fb'])) { + $return['fb_devel']['profile_id'] = array( + '#type' => 'markup', + '#markup' => __FUNCTION__ . ':' . print_r($data['fb']->api($data['profile_id']), 1), + '#prefix' => '
',
+        '#suffix' => '
', + '#weight' => 99, + ); + } + } +} + + +/** + * Wrapper for fb_fql_query(). Useful for debugging. + */ +function fb_devel_fql_query($fb, $query, $params = array()) { + if (!is_object($fb)) { + dpm(debug_backtrace(), __FUNCTION__); + return; + } + $url_params = $params; + $url_params['query'] = $query; + $url_params['format'] = 'JSON'; + $url = url("https://api.facebook.com/method/fql.query", array('query' => $url_params)); + dpm(func_get_args(), __FUNCTION__); + drupal_set_message(l($url, $url)); + return fb_fql_query($fb, $query, $params); +} diff --git a/fb_form.info b/fb_form.info index 08132db..753abfa 100644 --- a/fb_form.info +++ b/fb_form.info @@ -1,5 +1,11 @@ -name = FB Forms -description = (fb_form.module) Build FBML form constructs such as requests using Drupal's Form API. -package = Drupal for Facebook -dependencies[] = fb -core = 6.x \ No newline at end of file +name = FB Forms +description = (fb_form.module) Build FBML form constructs such as requests using Drupal's Form API. +package = Drupal for Facebook +dependencies[] = fb +core = 7.x +; Information added by drupal.org packaging script on 2011-06-23 +version = "7.x-4.x-dev" +core = "7.x" +project = "fb" +datestamp = "1308787966" + diff --git a/fb_stream.info b/fb_stream.info index 2b3cba2..407cb34 100644 --- a/fb_stream.info +++ b/fb_stream.info @@ -1,5 +1,11 @@ -name = FB Streams -description = (fb_stream.module) APIs for posting to Facebook Streams (a.k.a. News Feed or Wall) -package = Drupal for Facebook -dependencies[] = fb -core = 6.x \ No newline at end of file +name = FB Streams +description = (fb_stream.module) APIs for posting to Facebook Streams (a.k.a. News Feed or Wall) +package = Drupal for Facebook +dependencies[] = fb +core = 7.x +; Information added by drupal.org packaging script on 2011-06-23 +version = "7.x-4.x-dev" +core = "7.x" +project = "fb" +datestamp = "1308787966" + diff --git a/fb_tab.info b/fb_tab.info index ec1eb33..9fccc69 100644 --- a/fb_tab.info +++ b/fb_tab.info @@ -1,11 +1,17 @@ -name = FB Tabs -description = (fb_tab.module) Enable tabs on pages where your applications are installed. -package = Drupal for Facebook -core = 7.x - -dependencies[] = fb -dependencies[] = fb_app - -files[] = fb_tab.install -files[] = fb_tab.module -files[] = fb_tab.admin.inc \ No newline at end of file +name = FB Tabs +description = (fb_tab.module) Enable tabs on pages where your applications are installed. +package = Drupal for Facebook +core = 7.x + +dependencies[] = fb +dependencies[] = fb_app + +files[] = fb_tab.install +files[] = fb_tab.module +files[] = fb_tab.admin.inc +; Information added by drupal.org packaging script on 2011-06-23 +version = "7.x-4.x-dev" +core = "7.x" +project = "fb" +datestamp = "1308787966" + diff --git a/fb_user.info b/fb_user.info index c433bbe..f40ce80 100644 --- a/fb_user.info +++ b/fb_user.info @@ -1,12 +1,18 @@ -name= FB User Management -description = (fb_user.module) Maps local user accounts to facebook user ids. Creates local accounts for app users when configured to do so. - -package = Drupal for Facebook -core = 7.x - -dependencies[] = fb -dependencies[] = fb_app - -files[] = fb_user.install -files[] = fb_user.module -files[] = fb_user.admin.inc \ No newline at end of file +name= FB User Management +description = (fb_user.module) Maps local user accounts to facebook user ids. Creates local accounts for app users when configured to do so. + +package = Drupal for Facebook +core = 7.x + +dependencies[] = fb +dependencies[] = fb_app + +files[] = fb_user.install +files[] = fb_user.module +files[] = fb_user.admin.inc +; Information added by drupal.org packaging script on 2011-06-23 +version = "7.x-4.x-dev" +core = "7.x" +project = "fb" +datestamp = "1308787966" + diff --git a/fb_views/fb_views.info b/fb_views/fb_views.info index a8cd5da..91d0fe8 100644 --- a/fb_views/fb_views.info +++ b/fb_views/fb_views.info @@ -1,7 +1,14 @@ -name = FB Views -description = (fb_views.module) Provides features specific to Views module for Facebook Apps. -package= Drupal for Facebook -core = 6.x -dependencies[] = fb -dependencies[] = fb_user -dependencies[] = views +name = FB Views +description = (fb_views.module) Provides features specific to Views module for Facebook Apps. +package= Drupal for Facebook +core = 7.x +dependencies[] = fb +dependencies[] = fb_user +dependencies[] = views + +; Information added by drupal.org packaging script on 2011-06-23 +version = "7.x-4.x-dev" +core = "7.x" +project = "fb" +datestamp = "1308787966" +