You are here

fb_canvas.module in Drupal for Facebook 7.3

This module provides support for Canvas page applications. Use Drupal to power traditional Facebook Apps.

See also fb_connect.module for Facebook Connect.

File

fb_canvas.module
View source
<?php

/**
 * @file
 *
 * This module provides support for Canvas page applications.  Use
 * Drupal to power traditional Facebook Apps.
 *
 * See also fb_connect.module for Facebook Connect.
 *
 */

// Option to require_login() on all canvas pages.
define('FB_CANVAS_OPTION_ALLOW_ANON', 1);
define('FB_CANVAS_OPTION_REQUIRE_LOGIN', 2);
define('FB_CANVAS_VAR_PROCESS_FBML', 'fb_canvas_process_fbml');
define('FB_CANVAS_VAR_PROCESS_FBML_FORM', 'fb_canvas_process_fbml_form');
define('FB_CANVAS_VAR_PROCESS_IFRAME', 'fb_canvas_process_iframe');
define('FB_CANVAS_VAR_PROCESS_ABSOLUTE', 'fb_canvas_process_absolute_links');

/**
 * Implements hook_menu().
 */
function fb_canvas_menu() {
  $items = array();

  // Admin pages
  $items[FB_PATH_ADMIN . '/fb_canvas'] = array(
    'title' => 'Canvas Pages',
    'description' => 'Configure Canvas Pages',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'fb_canvas_admin_settings',
    ),
    'access arguments' => array(
      FB_PERM_ADMINISTER,
    ),
    'file' => 'fb_canvas.admin.inc',
    'type' => MENU_LOCAL_TASK,
  );
  return $items;
}

/**
 * Implements hook_fb().
 */
function fb_canvas_fb($op, $data, &$return) {
  $original_uid =& drupal_static(__FUNCTION__);
  global $user;
  $fb = isset($data['fb']) ? $data['fb'] : NULL;
  $fb_app = isset($data['fb_app']) ? $data['fb_app'] : NULL;
  if ($op == FB_OP_CURRENT_APP) {
    if (function_exists('fb_settings')) {
      if (fb_settings(FB_SETTINGS_TYPE) == FB_SETTINGS_TYPE_CANVAS) {

        // fb_settings.inc has determined this is a canvas page.
        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' => $app_key,
            ));
          }
          if (!$fb_app) {

            // DEPRECATED.  For backward compatibility, accept label in FB_SETTINGS_CB
            $fb_app = fb_get_app(array(
              'label' => $app_key,
            ));
          }
        }
        elseif ($id = fb_settings(FB_SETTINGS_ID)) {

          // New SDK includes ID when session is present.
          $fb_app = fb_get_app(array(
            'id' => $id,
          ));
        }
        elseif ($apikey = fb_settings(FB_SETTINGS_APIKEY)) {

          // Old SDK tells us APIKEY.  Deprecated.
          $fb_app = fb_get_app(array(
            'apikey' => $apikey,
          ));
        }
      }
    }
    if ($fb_app) {
      $return = $fb_app;
    }
  }
  elseif ($op == FB_OP_INITIALIZE) {

    // Get our configuration settings.
    $fb_canvas_data = _fb_canvas_get_config($fb_app);
    $is_canvas = FALSE;
    $use_ob = FALSE;
    if (fb_canvas_is_iframe()) {

      // Use buffer to process iframe markup.
      $is_canvas = TRUE;
      $use_ob = variable_get(FB_CANVAS_VAR_PROCESS_IFRAME, TRUE);
    }

    // Store entire page in output buffer.  Will post-process on exit.
    if ($use_ob) {
      ob_start();
      $GLOBALS['fb_canvas_post_process'] = TRUE;
    }
    if ($is_canvas && current_path() == variable_get('site_frontpage', 'node')) {
      if ($fb
        ->getUser()) {
        $front = $fb_canvas_data['front_added'];
      }
      else {
        $front = $fb_canvas_data['front_anonymous'];
      }
      if ($front) {
        menu_set_active_item(drupal_get_normal_path($front));
      }
    }
  }
  elseif ($op == FB_OP_POST_INIT) {
    if (fb_canvas_is_iframe()) {

      // The ?destination=... url param means something to drupal but something
      // else to facebook.  If ?fb_canvas_destination=... is set, we honor that.
      if (isset($_REQUEST['fb_canvas_destination'])) {
        $_REQUEST['destination'] = $_REQUEST['fb_canvas_destination'];
      }

      // Include our javascript.
      drupal_add_js(array(
        'fb_canvas' => array(
          'fbu' => fb_facebook_user(),
          'uid' => $GLOBALS['user']->uid,
          'canvas' => $fb_app->canvas,
        ),
      ), 'setting');
      drupal_add_js(drupal_get_path('module', 'fb_canvas') . '/fb_canvas.js');
    }

    // Include our admin hooks.
    if (fb_is_fb_admin_page()) {
      require drupal_get_path('module', 'fb_canvas') . '/fb_canvas.admin.inc';
    }
  }
  elseif ($op == FB_OP_EXIT) {

    /* We do some unpleasant stuff in this hook... on FBML canvas
           pages we might use $fb->redirect(), in which case other
           modules' hook_exit() might not be called.

           In other cases we call drupal_goto(), in which case other
           modules' hook_exit() might be called twice.  I hate to do this
           but so far have not figured another way.  And so far no
           problems... if problems arise, please post to issue queue.
        */
    $destination = $return;
    if (isset($GLOBALS['fb_canvas_post_process']) && $GLOBALS['fb_canvas_post_process']) {
      $output = ob_get_contents();
      ob_end_clean();
      if (fb_canvas_is_iframe()) {
        include_once drupal_get_path('module', 'fb') . '/fb.process.inc';
        $output = fb_process($output, array(
          'add_target' => '_top',
          'absolute_links' => variable_get(FB_CANVAS_VAR_PROCESS_ABSOLUTE, TRUE),
          'to_canvas' => $fb_app->canvas,
        ));
      }
    }
    if (fb_canvas_is_iframe() && !isset($GLOBALS['_fb_canvas_goto'])) {
      if ($destination) {

        // Fully qualified URLs need to be modified to point to facebook app.
        // URLs are fully qualified when a form submit handler returns a path,
        // or any call to drupal_goto.
        $app_destination = fb_canvas_fix_url($destination, $fb_app);

        // If here, drupal_goto has been called, but it may not work within a
        // canvas page, so we'll use Facebook's method.
        // Unfortunately, other modules' hook_exit() may not be called.
        if (fb_verbose()) {
          watchdog('fb_debug', "FB_OP_EXIT on canvas page redirecting to {$app_destination} (original destination was {$destination}).");
        }
        fb_iframe_redirect($app_destination);
      }
    }
    if (isset($output)) {
      print $output;
    }
  }
}

/**
 * Is the current request being displayed in an iframe canvas page?
 */
function fb_canvas_is_iframe() {

  // Use either parameters passed from facebook, or url rewriting.
  return fb_settings(FB_SETTINGS_TYPE) == FB_SETTINGS_TYPE_CANVAS;
}

/**
 * Helper returns configuration for this module, on a per-app basis.
 */
function _fb_canvas_get_config($fb_app) {
  $fb_app_data = fb_get_app_data($fb_app);
  $fb_canvas_data = isset($fb_app_data['fb_canvas']) ? $fb_app_data['fb_canvas'] : array();

  // Merge in defaults
  $fb_canvas_data += array(
    'require_login' => FB_CANVAS_OPTION_ALLOW_ANON,
    // @TODO - can this still be supported?
    'front_anonymous' => NULL,
    'front_loggedin' => NULL,
    // Facebook API no longer supports this.
    'front_added' => NULL,
  );
  return $fb_canvas_data;
}

/**
 * Implements hook_form_alter.
 */
function fb_canvas_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_canvas_admin_form_alter($form, $form_state, $form_id);
  }
  if (!empty($_REQUEST['signed_request']) && empty($form['signed_request']) && fb_is_canvas()) {

    // Facebook will pass our canvas pages 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'],
    );
  }
}

/**
 * Uses javascript on iframe canvas pages change top frame, otherwise drupal_goto().
 *
 * @see drupal_goto()
 */
function fb_canvas_goto($path) {
  global $_fb, $_fb_app;
  if ($_fb && fb_canvas_is_iframe()) {
    $url = fb_canvas_fix_url(url($path, array(
      'absolute' => TRUE,
    )), $_fb_app);

    // Allow modules to react to the end of the page request before redirecting.
    // We do not want this while running update.php.
    if (!defined('MAINTENANCE_MODE') || MAINTENANCE_MODE != 'update') {
      $GLOBALS['_fb_canvas_goto'] = TRUE;

      // prevents fb_canvas_exit from calling redirect.
      module_invoke_all('exit', $url);
    }
    fb_iframe_redirect($url);
  }
  else {
    drupal_goto($path);
  }
  exit;
}

/**
 * Define custom_url_rewrite_outbound() if not defined already.
 *
 * The bulk of URL rewriting is performed in fb_url_rewrite.inc.  That file
 * should be included in settings.php.  The url rewriting below was originally
 * an attempt to define those function here, only for canvas pages.  That
 * turned out to not be possible, so now the function just changes the
 * destination parameter to not confict with facebook's parameter of the same
 * name.
 *
 * For best results, admins should include fb_url_rewrite in their settings.php.
 *
 * @see fb_url_rewrite.inc
 */

/* not needed D7
if (!function_exists('custom_url_rewrite_outbound')) {
  function custom_url_rewrite_outbound(&$path, &$options, $original_path) {
    fb_canvas_url_outbound_alter($path, $options, $original_path);
  }
}
*/

/**
 * Implements hook_url_outbound_alter().
 *
 * @param $options
 *   If $options['fb_canvas'] == TRUE, create an absolute URL to a canvas
 *   page.  The URL will begin http://apps.facebook.com/...  Also if
 *   $options['fb_canvas'] is an application label the url will link to that
 *   particular application.
 */
function fb_canvas_url_outbound_alter(&$path, &$options, $original_path) {

  // use $options['fb_url_alter'] = FALSE to suppress any alteration.
  if (isset($options['external']) && $options['external'] || isset($options['fb_url_alter']) && $options['fb_url_alter'] === FALSE) {
    return;
  }
  if (isset($options['fb_canvas']) && is_string($options['fb_canvas'])) {
    $fb_app = fb_get_app(array(
      'label' => $options['fb_canvas'],
    ));
  }
  else {
    $fb_app = isset($GLOBALS['_fb_app']) ? $GLOBALS['_fb_app'] : NULL;
  }
  if ($fb_app && isset($fb_app->canvas)) {
    if (isset($options['fb_canvas']) && $options['fb_canvas']) {

      // Make a url starting with apps.facebook.com/...
      $options[FB_SETTINGS_CB] = FALSE;

      // prevent fb_url_rewrite.inc from inserting 'fb_cb'.
      $options['absolute'] = TRUE;
      $options['base_url'] = fb_protocol() . "://apps.facebook.com/{$fb_app->canvas}";
    }
    if (fb_canvas_is_iframe()) {
      if (empty($options['absolute'])) {

        // Could append session param to internal links.  But for now we rely on fb_process().
      }
      else {

        //dpm($options, "fb_canvas_url_outbound_alter($path)");
      }

      // Drupal has a habit of adding ?destination=... to some URLs.
      // And Facebook for no good reason screws up when you do that.
      if (!empty($options['query'])) {
        $options['query'] = str_replace('destination=', 'fb_canvas_destination=', $options['query']);
      }
    }
  }
}

/*
 * Implements hook_custom_theme().
 *
 * For canvas pages we use app-specific theme.  For this function to succeed,
 * fb_canvas.module must come after fb.module in the module weights (it is by
 * default).
 */
function fb_canvas_custom_theme() {
  if (fb_canvas_is_iframe()) {

    // Get our configuration settings.
    $fb_canvas_data = _fb_canvas_get_config($GLOBALS['_fb_app']);
    if ($custom_theme = $fb_canvas_data['theme_iframe']) {
      return $custom_theme;
    }
  }
}

/**
 * Implements hook_preprocess_html().
 *
 * Add canvas page specific body classes.
 */
function fb_canvas_preprocess_html(&$variables) {
  if (fb_is_canvas()) {
    $variables['classes_array'][] = 'fb_canvas-resizable';
    $variables['classes_array'][] = 'fb-canvas';
  }
}

Functions

Namesort descending Description
fb_canvas_custom_theme
fb_canvas_fb Implements hook_fb().
fb_canvas_form_alter Implements hook_form_alter.
fb_canvas_goto Uses javascript on iframe canvas pages change top frame, otherwise drupal_goto().
fb_canvas_is_iframe Is the current request being displayed in an iframe canvas page?
fb_canvas_menu Implements hook_menu().
fb_canvas_preprocess_html Implements hook_preprocess_html().
fb_canvas_url_outbound_alter Implements hook_url_outbound_alter().
_fb_canvas_get_config Helper returns configuration for this module, on a per-app basis.

Constants