fb_connect.module in Drupal for Facebook 5.2
Same filename and directory in other branches
Support for Facebook Connect features
Note that Facebook connect will work properly only with themes that are Facebook Connect aware.
File
fb_connect.moduleView source
<?php
/**
* @file
* Support for Facebook Connect features
*
* Note that Facebook connect will work properly only with themes that are
* Facebook Connect aware.
*/
function fb_connect_menu($may_cache) {
$items = array();
if ($may_cache) {
$items[] = array(
'path' => 'fb_connect/receiver',
'callback' => 'fb_connect_receiver',
'type' => MENU_CALLBACK,
'access' => TRUE,
);
}
return $items;
}
/**
* Without a receiver file, cross-domain javascript will not work.
*
* In their infinite wisdom, facebook has decreed that the URL for
* this static page be in the same place as the app's callback URL.
* So we have to make a Drupal callback for what would otherwise be a
* simple file.
*/
function fb_connect_receiver() {
$output = '
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:fb="http://www.facebook.com/2008/fbml">
<body>
<!-- http://wiki.developers.facebook.com/index.php/Cross_Domain_Communication_Channel -->
<script src="http://static.ak.connect.facebook.com/js/api_lib/v0.4/XdCommReceiver.debug.js" type="text/javascript"></script>
</body>
</html>
';
print $output;
exit;
die;
}
/**
* Prepare for fbConnect use. Because a single Drupal might support
* multiple apps, we don't know in advance which is the fbConnect app.
* Theoretically, it might be possible to simultaneously use multiple
* apps and fbConnect, but my suspicion is facebook would throw a
* total hissy-fit if you tried.
*/
function fb_connect_app_init($fb_app) {
if ($fb = fb_api_init($fb_app, FB_FBU_CURRENT)) {
return $fb;
}
}
/**
* Which apps are fbConnect enabled?
*/
function fb_connect_enabled_apps() {
// We do a bit of work for each enabled app, so really we want to restrict this list to only apps which have been "turned on".
// But for now we're lazy and just get the list of all apps.
$apps = fb_get_all_apps();
return $apps;
}
/**
* Implementation of hook_fb().
*/
function fb_connect_fb($op, $data, &$return) {
//dpm(func_get_args(), "fb_connect_fb($op)");
if ($op == FB_OP_CURRENT_APP && !$return) {
// This will cause fb.module to set the global $fb when user is logged in via fbConnect.
if ($apikey = variable_get('fb_connect_primary_apikey', NULL)) {
$return = fb_get_app(array(
'apikey' => $apikey,
));
}
}
else {
if ($op == FB_OP_POST_INIT) {
if ($apikey = variable_get('fb_connect_primary_apikey', NULL)) {
if ($data['fb_app']->apikey == $apikey) {
// Init Facebook javascript whenever logged into fbConnect
fb_connect_require_feature('XFBML', $fb_app);
// fb_connect_init_option('reloadIfSessionStateChanged', TRUE, $fb_app);
fb_connect_init_option('ifUserConnected', "{fb_connect_on_connected}", $fb_app);
fb_connect_init_option('ifUserNotConnected', "{fb_connect_on_not_connected}", $fb_app);
}
}
}
}
}
/**
* Implementation of hook_user
*
* On logout, redirect the user so facebook can expire their session.
* Should be a facebook API to do this, but there's none I know of.
*/
function fb_connect_user($op, &$edit, &$account, $category = NULL) {
if ($op == 'logout') {
if (isset($GLOBALS['fb_connect_apikey'])) {
global $fb, $fb_app;
try {
if ($fb && $fb->api_client->session_key) {
// We will log out of facebook in hook_exit.
$GLOBALS['fb_connect_logging_out'] = TRUE;
}
} catch (Exception $e) {
fb_log_exception($e, t('Failed to log out of fbConnect session'));
}
}
}
}
function fb_connect_exit($url = NULL) {
if (isset($GLOBALS['fb_connect_logging_out'])) {
global $fb;
session_write_close();
// drupal_goto calls this, so why not us?
if (!isset($url)) {
$url = url('<front>', NULL, NULL, TRUE);
}
$fb
->logout($url);
}
}
/**
* Allows other modules to specify which Facebook Connect features are
* required. This will affect how the FB_RequireFeatures javascript method is
* called.
*/
function fb_connect_require_feature($feature = NULL, $fb_app = NULL) {
if ($feature && !isset($fb_app)) {
$fb_app = $GLOBALS['fb_app'];
}
// some features may apply without an app, but for now let's enforce that an app is required.
if ($feature && !$fb_app) {
return;
}
static $features;
if (!$features) {
$features = array();
}
if ($fb_app && !$features[$fb_app->apikey]) {
$features[$fb_app->apikey] = array(
'fb_app' => $fb_app,
'features' => array(),
);
}
if ($feature) {
$features[$fb_app->apikey]['features'][$feature] = $feature;
}
return $features;
}
/**
* Add an option when initializing facebook's javascript api.
*/
function fb_connect_init_option($option = NULL, $value = NULL, $fb_app = NULL) {
if ($option && !isset($fb_app)) {
$fb_app = $GLOBALS['fb_app'];
}
if ($option && !$fb_app) {
return;
}
static $options;
if (!$options) {
$options = array();
}
if ($fb_app && !$options[$fb_app->apikey]) {
$options[$fb_app->apikey] = array();
}
if ($option) {
$options[$fb_app->apikey][$option] = $value;
}
return $options;
}
/**
* Include facebook javascript in the footer of the current page.
*/
function fb_connect_footer($is_front) {
// Do nothing on FBML pages
if (function_exists('fb_canvas_is_fbml') && fb_canvas_is_fbml()) {
return;
}
global $base_path;
$feature_data = fb_connect_require_feature();
$option_data = fb_connect_init_option();
if (count($feature_data)) {
foreach ($feature_data as $data) {
// Give other modules a chance to add javascript which executes after init.
$extra = fb_invoke(FB_OP_CONNECT_JS_INIT, $data, array());
$extra_js = implode("\n", $extra);
$fb_app = $data['fb_app'];
$features = $data['features'];
$options = json_encode($option_data[$fb_app->apikey]);
// Hack! What's the way to json_encode a function name?
$options = str_replace('"{', '', $options);
$options = str_replace('}"', '', $options);
// drupal_add_js cannot add external javascript, so we use hook_footer instead.
$output = '<script src="http://static.ak.connect.facebook.com/js/api_lib/v0.4/FeatureLoader.js.php" type="text/javascript"></script>';
$output .= "\n";
$feature_list = '["' . implode('","', $features) . '"]';
// Put together the URL for the receiver. The prefix must be identical to the apps callback URL.
$receiver = fb_get_callback_url($fb_app) . "fb_connect/receiver";
$output .= "\n<script type=\"text/javascript\">\n \$(document).ready(function() {\n FB_RequireFeatures({$feature_list}, function () {\n\n //FB.FBDebug.logLevel = 4;\n //FB.FBDebug.isEnabled = true;\n\n FB.init(\"{$fb_app->apikey}\", \"{$receiver}\", {$options});\n });\n });\n";
// Extra JS after successful fbConnect init.
$output .= "\n FB.ensureInit(function()\n {\n fb_connect_init();\n {$extra_js}\n });\n";
$output .= "\n</script>\n";
}
}
return $output;
}
function _fb_connect_block_login_defaults() {
return array(
'anon_not_connected' => array(
'title' => t('Facebook Connect'),
'body' => t('Facebook users login here. !button', array(
'!button' => "<fb:login-button onclick='fb_connect_login_onclick();'></fb:login-button>",
)),
),
'user_not_connected' => array(
'title' => t('Facebook Connect'),
'body' => t('Link your account with Facebook. !button', array(
'!button' => "<fb:login-button onclick='fb_connect_login_onclick();'></fb:login-button>",
)),
),
'connected' => array(
'title' => t('Facebook Connect'),
'body' => "<fb:profile-pic uid=!fbu></fb:profile-pic><fb:login-button onclick='fb_connect_logout_onclick();' autologoutlink=true></fb:login-button>",
),
);
}
/**
* Implementation of hook_block.
*/
function fb_connect_block($op = 'list', $delta = 0, $edit = array()) {
if ($op == 'list') {
$items = array();
foreach (fb_connect_enabled_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;
}
else {
if ($op == 'configure') {
$defaults = variable_get('fb_connect_block_' . $delta, _fb_connect_block_login_defaults());
$form['config'] = array(
'#tree' => TRUE,
);
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'),
//'#description' => t('Default title.'),
'#default_value' => $defaults[$key]['title'],
);
$form['config'][$key]['body'] = array(
'#type' => 'textarea',
'#title' => t('Body'),
//'#description' => t('Block body'),
'#default_value' => $defaults[$key]['body'],
);
}
$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']['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']['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.');
$form['config']['connected']['body']['#description'] .= t('Note that <em>!fbu</em> will be replaced with the user\'s facebook id.');
$form['config']['filter'] = filter_form($defaults['filter']);
$form['config']['filter']['#description'] .= t('Format selected will apply to all body fields above. Be sure to select a format which allows FBML tags!');
$form['config']['filter']['#collapsed'] = FALSE;
return $form;
}
else {
if ($op == 'save') {
variable_set('fb_connect_block_' . $delta, $edit['config']);
}
else {
if ($op == 'view') {
if (strpos($delta, 'login_') === 0) {
// Login block
$label = substr($delta, 6);
// length of 'login_'
$fb_app = fb_get_app(array(
'label' => $label,
));
$fb = fb_connect_app_init($fb_app);
$fbu = $fb
->get_loggedin_user();
fb_connect_require_feature('XFBML', $fb_app);
//fb_connect_init_option('reloadIfSessionStateChanged', TRUE, $fb_app);
//fb_connect_init_option('doNotUseCachedConnectState', TRUE, $fb_app);
$base = drupal_get_path('module', 'fb_connect');
drupal_add_js(array(
'fb_connect' => array(
'fbu' => $fbu ? $fbu : 0,
// XXX
'logout_url' => url('logout'),
// XXX
'front_url' => url('<front>'),
'enable_login' => TRUE,
),
), 'setting');
drupal_add_js($base . '/fb_connect.js');
$defaults = variable_get('fb_connect_block_' . $delta, _fb_connect_block_login_defaults());
if ($fbu) {
$subject = $defaults['connected']['title'];
$content = $defaults['connected']['body'];
// substitute %fbu
$content = str_replace('!fbu', $fbu, $content);
}
else {
if ($GLOBALS['user']->uid) {
$subject = $defaults['user_not_connected']['title'];
$content = $defaults['user_not_connected']['body'];
}
else {
$subject = $defaults['anon_not_connected']['title'];
$content = $defaults['anon_not_connected']['body'];
}
}
// If user has changed defaults, run filter
if ($defaults['filter']) {
$subject = check_plain($subject);
$content = check_markup($content, $default['filter'], FALSE);
}
$block = array(
'subject' => $subject,
'content' => $content,
);
return $block;
}
}
}
}
}
}
function fb_connect_form_alter($form_id, &$form) {
// Add our settings to the fb_app edit form.
if (is_array($form['fb_app_data'])) {
$node = $form['#node'];
$fb_app_data = fb_app_get_data($node->fb_app);
$fb_connect_data = $fb_app_data['fb_connect'];
$form['fb_app_data']['fb_connect'] = array(
'#type' => 'fieldset',
'#title' => 'Facebook Connect',
'#tree' => TRUE,
'#collapsible' => TRUE,
'#collapsed' => $node->nid ? TRUE : FALSE,
);
$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' => $fb_connect_data['primary'],
);
if ($primary_apikey = variable_get('fb_connect_primary_apikey', NULL)) {
if ($primary_apikey != $node->fb_app->apikey) {
$primary = fb_get_app(array(
'apikey' => $primary_apikey,
));
$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 ? $primary->title : $primary_apikey,
));
}
}
}
}
function fb_connect_nodeapi(&$node, $op, $a3 = NULL, $a4 = NULL) {
if (($op == 'insert' || $op == 'update') && $node->type == 'fb_app') {
//dpm(func_get_args(), "fb_connect_nodeapi($op)"); // debug
if ($node->fb_app_data['fb_connect']['primary']) {
variable_set('fb_connect_primary_apikey', $node->fb_app['apikey']);
drupal_set_message(t('!node is now the primary Facebook Connect application.', array(
'!node' => l($node->title, 'node/' . $node->nid),
)));
}
}
}
Functions
Name | Description |
---|---|
fb_connect_app_init | Prepare for fbConnect use. Because a single Drupal might support multiple apps, we don't know in advance which is the fbConnect app. Theoretically, it might be possible to simultaneously use multiple apps and fbConnect, but my suspicion is… |
fb_connect_block | Implementation of hook_block. |
fb_connect_enabled_apps | Which apps are fbConnect enabled? |
fb_connect_exit | |
fb_connect_fb | Implementation of hook_fb(). |
fb_connect_footer | Include facebook javascript in the footer of the current page. |
fb_connect_form_alter | |
fb_connect_init_option | Add an option when initializing facebook's javascript api. |
fb_connect_menu | @file Support for Facebook Connect features |
fb_connect_nodeapi | |
fb_connect_receiver | Without a receiver file, cross-domain javascript will not work. |
fb_connect_require_feature | Allows other modules to specify which Facebook Connect features are required. This will affect how the FB_RequireFeatures javascript method is called. |
fb_connect_user | Implementation of hook_user |
_fb_connect_block_login_defaults |