fb_tab.module in Drupal for Facebook 7.3
Same filename and directory in other branches
This module provides support for "Profile Tabs" that can be added to facebook pages (no longer allowed for user profiles).
File
fb_tab.moduleView source
<?php
/**
* @file
*
* This module provides support for "Profile Tabs" that can be added to
* facebook pages (no longer allowed for user profiles).
*
* http://developers.facebook.com/docs/guides/canvas/#tabs
*
*/
// hook_fb
define('FB_TAB_HOOK', 'fb_tab');
//// Hook_fb_tab operations
define('FB_TAB_OP_VIEW', 'fb_tab_view');
define('FB_TAB_OP_FORM', 'fb_tab_form');
define('FB_TAB_PATH_VIEW', 'fb_tab/view');
define('FB_TAB_PATH_FORM', 'fb_tab/config');
define('FB_TAB_PATH_FORM_ARGS', 2);
define('FB_TAB_PATH_ADDED', 'fb_tab/added');
// user redirected here after adding page tab.
define('FB_TAB_PATH_ADDED_ARGS', 2);
define('FB_TAB_VAR_PROCESS_IFRAME', 'fb_tab_process_iframe');
define('FB_TAB_VAR_PROCESS_ABSOLUTE', 'fb_tab_process_absolute_links');
define('FB_TAB_VAR_PROCESS_TO_CANVAS', 'fb_tab_process_to_canvas');
define('FB_TAB_PROCESS_IFRAME_NONE', 'none');
define('FB_TAB_PROCESS_IFRAME_TO_CANVAS', 'canvas');
define('FB_TAB_PROCESS_IFRAME_TO_TAB_VIA_APP_DATA', 'app_data');
function fb_tab_menu() {
$items = array();
$items[FB_TAB_PATH_VIEW] = array(
'page callback' => 'fb_tab_view',
'access arguments' => array(
'access content',
),
'type' => MENU_CALLBACK,
);
$items[FB_TAB_PATH_FORM] = array(
'title' => 'Configure Tabs',
'page callback' => 'fb_tab_pages',
'access arguments' => array(
'access content',
),
'type' => MENU_CALLBACK,
);
$items[FB_TAB_PATH_FORM . '/%'] = array(
'page callback' => 'drupal_get_form',
'page arguments' => array(
'fb_tab_config_form',
FB_TAB_PATH_FORM_ARGS,
),
'access arguments' => array(
'access content',
),
'type' => MENU_CALLBACK,
);
$items[FB_TAB_PATH_ADDED . '/%fb'] = array(
'page callback' => 'fb_tab_added',
'access arguments' => array(
'access content',
),
'page arguments' => array(
FB_TAB_PATH_ADDED_ARGS,
),
'type' => MENU_CALLBACK,
);
// Admin pages
$items[FB_PATH_ADMIN . '/fb_tab'] = array(
'title' => 'Page Tabs',
'description' => 'Configure Tabs',
'page callback' => 'drupal_get_form',
'page arguments' => array(
'fb_tab_admin_settings',
),
'access arguments' => array(
FB_PERM_ADMINISTER,
),
'file' => 'fb_tab.admin.inc',
'type' => MENU_LOCAL_TASK,
);
return $items;
}
/*
* Implements hook_custom_theme().
*
* For tab pages we use app-specific theme. For this function to succeed,
* fb_tab.module must come after fb.module in the module weights (it is by
* default).
*/
function fb_tab_custom_theme() {
if (fb_is_tab()) {
// Get our configuration settings.
$config = _fb_tab_get_app_config($GLOBALS['_fb_app']);
if ($custom_theme = $config['custom_theme']) {
return $custom_theme;
}
}
}
/**
* Implements hook_fb
*
* @param $op
* @param $data -- data payload
* @param $return
*/
function fb_tab_fb($op, $data, &$return) {
$fb = isset($data['fb']) ? $data['fb'] : NULL;
$fb_app = isset($data['fb_app']) ? $data['fb_app'] : NULL;
if ($op == FB_OP_POST_INIT) {
// Include our admin hooks.
if (fb_is_fb_admin_page()) {
require drupal_get_path('module', 'fb_tab') . '/fb_tab.admin.inc';
}
if (fb_is_tab()) {
// Include our javascript.
drupal_add_js(array(
'fb_tab' => array(
'fbu' => fb_facebook_user(),
'uid' => $GLOBALS['user']->uid,
'canvas' => $fb_app->canvas,
),
), 'setting');
drupal_add_js(drupal_get_path('module', 'fb_tab') . '/fb_tab.js');
}
}
elseif ($op == FB_OP_CURRENT_APP && fb_is_tab()) {
if ($id = fb_settings(FB_SETTINGS_CB)) {
// Using fb_url_rewrite.
$fb_app = fb_get_app(array(
'id' => $id,
));
if (!$fb_app) {
// DEPRECATED. For backward compatibility, accept apikey in FB_SETTINGS_CB
$fb_app = fb_get_app(array(
'apikey' => $id,
));
}
}
elseif ($id = fb_settings(FB_SETTINGS_ID)) {
// New SDK includes ID when session is present.
$fb_app = fb_get_app(array(
'id' => $id,
));
}
elseif (isset($_REQUEST['fb_sig_api_key'])) {
$return = fb_get_app(array(
'apikey' => $_REQUEST['fb_sig_api_key'],
));
}
if ($fb_app) {
$return = $fb_app;
}
}
elseif ($op == FB_OP_INITIALIZE) {
if (fb_is_tab()) {
$config = _fb_tab_get_app_config($fb_app);
// custom_theme handling moved to fb_tab_custom_theme().
if (fb_settings(FB_SETTINGS_TYPE) == FB_SETTINGS_TYPE_PAGE_TAB && (variable_get(FB_TAB_VAR_PROCESS_IFRAME, TRUE) || variable_get(FB_TAB_VAR_PROCESS_ABSOLUTE, TRUE))) {
// Process iframe
$use_ob = TRUE;
}
else {
$use_ob = FALSE;
}
// Hack to init the theme before _drupal_maintenance_theme initializes the wrong one.
if (variable_get('maintenance_mode', FALSE)) {
$dummy = theme('dummy');
}
// Store entire page in output buffer. Will post-process on exit.
if ($use_ob) {
ob_start();
$GLOBALS['fb_tab_post_process'] = TRUE;
}
// If path is fb_tab/view, we may actually show some other menu item.
if (arg(0) == 'fb_tab' && arg(1) == 'view') {
// Do not override fb_tab/config.
$sr = $fb
->getSignedRequest();
if (!empty($sr['app_data']) && !empty($config['app_data_is_path'])) {
$path = $sr['app_data'];
}
elseif ($sr['page'] && $sr['page']['liked'] && $config['profile_tab_url_liked']) {
$path = $config['profile_tab_url_liked'];
}
else {
$path = $config['profile_tab_url'];
}
// Enable token replacement from signed request.
$path = token_replace($path, array(), array(
'clear' => TRUE,
));
// <front> is a special path
$path = str_replace('<front>', variable_get('site_frontpage', 'node'), $path);
if ($path && $path != FB_TAB_PATH_VIEW) {
// Tell Drupal what to really render.
menu_set_active_item(drupal_get_normal_path($path));
}
}
}
}
elseif ($op == FB_OP_EXIT && fb_is_tab()) {
if (isset($GLOBALS['fb_tab_post_process']) && $GLOBALS['fb_tab_post_process']) {
$output = ob_get_contents();
ob_end_clean();
include_once drupal_get_path('module', 'fb') . '/fb.process.inc';
$process = variable_get(FB_TAB_VAR_PROCESS_IFRAME, FB_TAB_PROCESS_IFRAME_TO_CANVAS);
if ($process == FB_TAB_PROCESS_IFRAME_TO_CANVAS) {
$to_canvas = $fb_app->canvas;
}
else {
$to_canvas = FALSE;
}
$to_page = FALSE;
$page = NULL;
if ($process == FB_TAB_PROCESS_IFRAME_TO_TAB_VIA_APP_DATA) {
if ($page_id = fb_settings(FB_SETTINGS_PAGE_ID)) {
$page = fb_graph($page_id);
if (!empty($page['link'])) {
// @TODO: $page['link'] may change protocol from https to http.
$to_page = $page['link'] . "?v=app_" . fb_settings(FB_SETTINGS_ID);
}
}
}
if (fb_settings(FB_SETTINGS_TYPE) == FB_SETTINGS_TYPE_PAGE_TAB) {
// Process iframe
$output = fb_process($output, array(
'add_target' => '_top',
'absolute_links' => variable_get(FB_TAB_VAR_PROCESS_ABSOLUTE, TRUE),
'relative_links' => variable_get(FB_TAB_VAR_PROCESS_IFRAME, TRUE),
'to_canvas' => $to_canvas,
'to_page' => $to_page,
));
}
elseif (fb_settings(FB_SETTINGS_TYPE) == FB_SETTINGS_TYPE_PROFILE) {
// Process FBML (deprecated)
$output = fb_process($output, array(
'add_target' => FALSE,
'absolute_links' => TRUE,
'to_canvas' => $to_canvas,
));
}
if (isset($output)) {
print $output;
}
}
$destination = $return;
if ($destination) {
// A normal redirect will affect only our iframe.
// We must instead send the user to our page on facebook.com.
if ($process == FB_TAB_PROCESS_IFRAME_TO_TAB_VIA_APP_DATA) {
if (!empty($to_page)) {
$url = fb_iframe_fix_url($destination, $to_page . '&app_data=');
// It's not ideal to call this here, as doing so prevents other
// hook_exits from running. However there's no later
// opportunity to change where Drupal would send us.
fb_iframe_redirect($url);
}
}
}
}
}
/**
* Implements fb_tab_form_alter.
*/
function fb_tab_form_alter(&$form, &$form_state, $form_id) {
if (isset($form['fb_app_data']) && is_array($form['fb_app_data'])) {
// Add our settings to the fb_app edit form.
//require 'fb_canvas.admin.inc';
fb_tab_admin_form_alter($form, $form_state, $form_id);
}
if (!empty($_REQUEST['signed_request']) && empty($form['signed_request']) && fb_is_tab()) {
// Facebook will pass us the important signed_request.
// When we submit a form, that data will be lost unless we explicitly include it in the form.
$form['signed_request'] = array(
'#type' => 'hidden',
'#value' => $_REQUEST['signed_request'],
);
}
}
/**
* Helper returns configuration for this module, on a per-app basis.
*/
function _fb_tab_get_app_config($fb_app) {
$fb_app_data = fb_get_app_data($fb_app);
$config = isset($fb_app_data['fb_tab']) ? $fb_app_data['fb_tab'] : array();
// Merge in defaults
$config += array(
'custom_theme' => NULL,
'tab_default_name' => isset($fb_app->title) ? $fb_app->title : NULL,
'profile_tab_url' => NULL,
'profile_tab_url_liked' => NULL,
'app_data_is_path' => TRUE,
'edit_url' => FB_TAB_PATH_FORM,
);
return $config;
}
/**
* Another module provides the contents of the tab depending
* on the context that we give it (who is calling it, etc.)
*/
function fb_tab_view() {
$fb_app = $GLOBALS['_fb_app'];
$fb = $GLOBALS['_fb'];
$profile_id = fb_settings(FB_SETTINGS_CB_PAGE);
$config = fb_tab_get_page_config($fb_app, $profile_id);
$sr = $fb
->getSignedRequest();
$data = array(
'fb_app' => $fb_app,
'fb' => $fb,
'profile_id' => $profile_id,
'config' => isset($config['data']) ? $config['data'] : NULL,
'page' => isset($sr['page']) ? $sr['page'] : NULL,
);
$content = fb_invoke(FB_TAB_OP_VIEW, $data, array(), FB_TAB_HOOK);
return drupal_render($content);
}
/**
* Build the tab config form. Invokes hook_fb_tab() to get the custom settings.
*/
function fb_tab_config_form($form_state, $profile_id) {
$fb_app = $GLOBALS['_fb_app'];
$fb = $GLOBALS['_fb'];
$config = fb_tab_get_page_config($fb_app, $profile_id);
$sr = $fb
->getSignedRequest();
$data = array(
'fb_app' => $fb_app,
'fb' => $fb,
'profile_id' => $profile_id,
'config' => isset($config['data']) ? $config['data'] : NULL,
'page' => $sr['page'],
);
try {
//$fbu = fb_facebook_user($fb);
$fbu = fb_require_authorization($fb);
$page_info = $fb
->api($profile_id, array(
'access_token' => fb_get_token($fb),
));
$admin_info = fb_fql_query($fb, "SELECT uid, type FROM page_admin WHERE uid={$fbu} AND page_id={$profile_id}");
// FQL not SQL, no {curly_brackets}
// @TODO: if not page admin, prompt user to login or deny access.
$form = array(
'info' => array(
'#type' => 'markup',
'#value' => t('Tab settings for <a href="!href">%page</a>', array(
'!href' => $page_info['link'],
'%page' => $page_info['name'],
)),
'#prefix' => '<h3>',
'#suffix' => '</h3>',
'#weight' => -99,
),
'label' => array(
'#type' => 'value',
'#value' => $fb_app->label,
),
'profile_id' => array(
'#type' => 'value',
'#value' => $profile_id,
),
'data' => array(
// Modules will add their fields here.
'#tree' => TRUE,
),
'submit' => array(
'#type' => 'submit',
'#value' => t('Submit'),
'#weight' => 90,
),
);
if (isset($config['fb_tab_id'])) {
$form['fb_tab_id'] = array(
'#type' => 'value',
'#value' => $config['fb_tab_id'],
);
}
$form['data'] = fb_invoke(FB_TAB_OP_FORM, $data, $form['data'], FB_TAB_HOOK);
//print('<pre>' . print_r($data, 1) . '</pre>'); flush();
//print(print_r($content, 1)); flush();
return $form;
} catch (Exception $e) {
fb_log_exception($e, t('Failed to get details for facebook page %profile_id', array(
'%profile_id' => $profile_id,
)));
return array(
'error' => array(
'#value' => t('Unable to set properties.'),
),
);
}
}
/**
* Submit handler for tab config form.
*/
function fb_tab_config_form_submit($form, &$form_state) {
if ($profile_id = $form_state['values']['profile_id']) {
fb_tab_write_config($form_state['values']);
$data = fb_fql_query($GLOBALS['_fb'], "SELECT page_id, name, pic_small, page_url, type FROM page WHERE page_id={$profile_id}");
// FQL, no {curly_brackets}
drupal_set_message(t('The tab settings for <a href="!url">%page</a> have been updated.', array(
'!url' => $data[0]['page_url'],
'%page' => $data[0]['name'],
)));
}
}
/*
* Store configuration data for a tab by application.
*/
function fb_tab_write_config(&$row) {
if (!isset($row['created'])) {
$row['created'] = time();
}
$row['data'] = serialize($row['data']);
return drupal_write_record('fb_tab', $row, isset($row['fb_tab_id']) ? array(
'fb_tab_id',
) : NULL);
}
/*
* Return configuration of a tab by application (access via 'label') and page ID.
* Page ID exists because an app can present different views on different pages.
*/
function fb_tab_get_page_config($fb_app, $profile_id) {
$row = db_query("SELECT * FROM {fb_tab} WHERE label = :label AND profile_id = :pid", array(
':label' => $fb_app->label,
':pid' => $profile_id,
))
->fetchAssoc();
if ($row) {
$row['data'] = unserialize($row['data']);
return $row;
}
else {
return array();
}
}
/**
* Drupal menu callback. User has added a tab to one of their pages.
*/
function fb_tab_added($fb_app) {
//dpm($_REQUEST, __FUNCTION__); // debug what facebook has passed to us
if (empty($_REQUEST['tabs_added'])) {
// Sanity check.
drupal_set_message(t('Failed to add tab to facebook page.'), 'error');
return t('Failed to add tab to facebook page.');
}
foreach ($_REQUEST['tabs_added'] as $page_id => $added) {
// TODO use batch graph api.
$graph = fb_graph($page_id, array());
watchdog('fb_tab', "Facebook application %label (%app_id) added to page %name (%page_id)", array(
'%label' => $fb_app->label,
'%app_id' => $fb_app->id,
'%name' => $graph['name'],
'%page_id' => $graph['id'],
));
}
// Redirect user to the page tab they've added.
if ($graph['link']) {
// This undocumented format seems to be the right URL for a page tab.
$url = $graph['link'] . '?sk=app_' . $fb_app->id;
drupal_goto($url);
}
// If drupal_goto succeeds, user will not see this.
return t('Added application to Facebook page.');
}
/**
* This page callback will show the user a list of pages they have authority
* to configure.
*/
function fb_tab_pages() {
if ($fbu = fb_facebook_user()) {
// @TODO: broken on facebook's end??? this query used to work.
$result = fb_fql_query($GLOBALS['_fb'], "SELECT page_id, name, pic_small, page_url, type FROM page WHERE has_added_app=1 AND page_id IN (SELECT page_id FROM page_admin WHERE uid={$fbu})");
// FQL, no {curly_brackets}
if (count($result)) {
$output['pages'] = array(
'#prefix' => '<ul>',
'#suffix' => '</ul>',
);
foreach ($result as $data) {
$output['pages'][$data['page_id']] = array(
'#value' => l($data['name'], fb_tab_config_url($data['page_id'], array(
'fb_canvas' => fb_is_canvas(),
))),
'#prefix' => '<li>',
'#suffix' => '</li>',
);
}
$output['intro'] = array(
'#value' => t('Select one of your pages to configure:'),
'#prefix' => '<p>',
'#suffix' => '</p>',
);
}
else {
$output['intro'] = array(
'#value' => t('Found no pages to configure.'),
'#prefix' => '<p>',
'#suffix' => '</p>',
);
}
return drupal_render($output);
}
else {
fb_access_denied();
}
}
/**
* Provides the URL of the settings page for a given facebook page.
*/
function fb_tab_config_url($profile_id = NULL, $options = array()) {
if (!$profile_id) {
$profile_id = fb_settings(FB_SETTINGS_CB_PAGE);
}
return url(FB_TAB_PATH_FORM . '/' . $profile_id, $options);
}
Functions
Name | Description |
---|---|
fb_tab_added | Drupal menu callback. User has added a tab to one of their pages. |
fb_tab_config_form | Build the tab config form. Invokes hook_fb_tab() to get the custom settings. |
fb_tab_config_form_submit | Submit handler for tab config form. |
fb_tab_config_url | Provides the URL of the settings page for a given facebook page. |
fb_tab_custom_theme | |
fb_tab_fb | Implements hook_fb |
fb_tab_form_alter | Implements fb_tab_form_alter. |
fb_tab_get_page_config | |
fb_tab_menu | |
fb_tab_pages | This page callback will show the user a list of pages they have authority to configure. |
fb_tab_view | Another module provides the contents of the tab depending on the context that we give it (who is calling it, etc.) |
fb_tab_write_config | |
_fb_tab_get_app_config | Helper returns configuration for this module, on a per-app basis. |