Skip to content
fb_connect.module 16 KiB
Newer Older
Dave Cohen's avatar
Dave Cohen committed
/**
 * @file
 * Support for Facebook Connect features
Dave Cohen's avatar
Dave Cohen committed
 *
Dave Cohen's avatar
Dave Cohen committed
 * Note that Facebook connect will work properly only with themes that are
 * Facebook Connect aware.
 */
// Drupal variables
define('FB_CONNECT_VAR_PRIMARY', 'fb_connect_primary_label');
define('FB_CONNECT_VAR_THEME_USERNAME_1', 'fb_connect_theme_username_1');
define('FB_CONNECT_VAR_THEME_USERNAME_2', 'fb_connect_theme_username_2');
Dave Cohen's avatar
Dave Cohen committed
define('FB_CONNECT_VAR_THEME_USERPIC_1', 'fb_connect_theme_userpic_1');
define('FB_CONNECT_VAR_THEME_USERPIC_2', 'fb_connect_theme_userpic_2');
function fb_connect_menu() {
  $items[FB_PATH_ADMIN . '/fb_connect'] = array(
    'title' => '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,
Dave Cohen's avatar
Dave Cohen committed

/**
 * 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;
  }
Dave Cohen's avatar
Dave Cohen committed

  if ($fb = fb_api_init($fb_app)) {
    $fbu = $fb->getUser();
        (!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));
Dave Cohen's avatar
Dave Cohen committed

Dave Cohen's avatar
Dave Cohen committed
    // Remember which app we've initialized.
    _fb_connect_set_app($fb_app);
    _fb_connect_add_js($fb_app, $fb);
Dave Cohen's avatar
Dave Cohen committed
/**
 * Helper function for other modules to know page is connected.
Dave Cohen's avatar
Dave Cohen committed
 *
Dave Cohen's avatar
Dave Cohen committed
 * 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__);
Dave Cohen's avatar
Dave Cohen committed
  if (isset($fb_app)) {
    $cache = $fb_app;
  }
  return $cache;
}

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 ($id = variable_get(FB_VAR_ID, NULL)) {
      // Use $conf['fb_id'] if set in settings.php.
      $return = fb_get_app(array('id' => $id));
    }
    elseif ($apikey = variable_get(FB_VAR_APIKEY, NULL)) { // Deprecated.  Use fb_id instead.
Dave Cohen's avatar
Dave Cohen committed
      // 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('<front>'),
                      'fbu' => fb_facebook_user(),
Dave Cohen's avatar
Dave Cohen committed
                      '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' => '<fb:login-button perms="!perms" v="2">Connect</fb:login-button>'),
               ),
               'user_not_connected' => array(
                 'title' => t('Facebook Connect'),
                 'body' => array('value' => '<fb:login-button perms="!perms" v="2">Connect</fb:login-button>'),
               ),
               'connected' => array(
                 'title' => t('Facebook Connect'),
                 // Logout commented out, because drupal has logout link.
                 'body' => array('value' => '<fb:profile-pic uid=!fbu></fb:profile-pic><!--<fb:login-button autologoutlink=true></fb:login-button>-->'),
 * 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' => "<p><strong>Be sure to select a format that allows XFBML tags!</strong> (That is, use <em>Full HTML</em> or <em>PHP code</em> (PHP Filter module must be enabled), rather than  <em>Filtered HTML</em>.)</p>",
    '#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 <a target="_blank" href="!xfbml_url">XFBML</a> such as &lt;fb:name uid=!fbu&gt;&lt;/fb:name&gt; or &lt;fb:profile-pic uid=!fbu&gt;&lt;/fb:profile-pic&gt;', array('!xfbml_url' => 'http://wiki.developers.facebook.com/index.php/XFBML'));
  $form['config']['connected']['body']['#description'] = t('Note that <strong>!fbu</strong> will be replaced with the user\'s facebook id.<br/>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.
Dave Cohen's avatar
Dave Cohen committed
    if (strpos($delta, 'login_') === 0) {
      // Login block
      $label = substr($delta, 6); // length of 'login_'
      $fb_app = fb_get_app(array('label' => $label));
Dave Cohen's avatar
Dave Cohen committed
      if ($fb_app &&
          ($fb = fb_connect_app_init($fb_app))) {
        //_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());
Dave Cohen's avatar
Dave Cohen committed
        }
        elseif ($GLOBALS['user']->uid >= 1) {
Dave Cohen's avatar
Dave Cohen committed
        }
        $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;
Dave Cohen's avatar
Dave Cohen committed
/**
 * 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();
Dave Cohen's avatar
Dave Cohen committed
  // 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'])) {
    $form['fb_app_data']['fb_connect'] = array(
      '#type' => 'fieldset',
      '#title' => 'Facebook connect',
      '#tree' => TRUE,
      '#collapsible' => TRUE,
Dave Cohen's avatar
Dave Cohen committed
      '#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)) {
Dave Cohen's avatar
Dave Cohen committed
      $form['fb_app_data']['fb_connect']['primary']['#description'] .= '<br/>' .
        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';
Dave Cohen's avatar
Dave Cohen committed
  }
/**
 * 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.
Dave Cohen's avatar
Dave Cohen committed
    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'];
  $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,
        ));
  }
  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,
        ));