You are here

function fb_canvas_fb in Drupal for Facebook 6.2

Same name and namespace in other branches
  1. 5.2 fb_canvas.module \fb_canvas_fb()
  2. 5 fb_canvas.module \fb_canvas_fb()
  3. 6.3 fb_canvas.module \fb_canvas_fb()
  4. 7.3 fb_canvas.module \fb_canvas_fb()

Implementation of hook_fb().

File

./fb_canvas.module, line 47
This module provides support for Canvas page applications. Use Drupal to power traditional Facebook Apps.

Code

function fb_canvas_fb($op, $data, &$return) {
  static $original_uid;
  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 && ($apikey = fb_settings(FB_SETTINGS_APIKEY))) {

        // This is the proper way to discover the app on canvas pages.
        $fb_app = fb_get_app(array(
          'apikey' => $apikey,
        ));

        // If were on an iframe excursion, the fb_sig will be in session.
        if (!isset($_REQUEST['fb_sig']) && isset($_SESSION['fb_canvas_fb_sig'])) {
          foreach ($_SESSION['fb_canvas_fb_params'] as $k => $value) {
            $key = 'fb_sig_' . $k;
            if (!isset($_REQUEST[$key])) {

              // We use $_REQUEST, facebook uses $_GET.
              $_REQUEST[$key] = $value;
              $_GET[$key] = $value;
            }
          }
          $_REQUEST['fb_sig'] = $_SESSION['fb_canvas_fb_sig'];
          $_GET['fb_sig'] = $_SESSION['fb_canvas_fb_sig'];
        }
      }
      elseif ($id = fb_settings(FB_SETTINGS_CB)) {

        // This is the old way dependent on url rewriting.
        $fb_app = fb_get_app(array(
          'id' => $id,
        ));
      }
    }
    else {

      // Fallback in case fb_settings not used.
      if (isset($_REQUEST[FB_APP_REQ_API_KEY]) && ($apikey = $_REQUEST[FB_APP_REQ_API_KEY])) {

        // If facebook has passed the app key, let's use that.
        $fb_app = fb_get_app(array(
          'apikey' => $apikey,
        ));
      }
    }
    if ($fb_app) {
      $return = $fb_app;
    }
  }
  elseif ($op == FB_OP_INITIALIZE) {

    // Get our configuration settings.
    $fb_app_data = fb_get_app_data($fb_app);
    $fb_canvas_data = $fb_app_data['fb_canvas'];
    $is_canvas = FALSE;

    // Set an app-specific theme.
    global $custom_theme;

    // Set by this function.
    if (fb_canvas_is_fbml()) {
      $custom_theme = $fb_canvas_data['theme_fbml'];
      $is_canvas = TRUE;
      $use_ob = variable_get(FB_CANVAS_VAR_PROCESS_FBML, TRUE);
    }
    elseif (fb_canvas_is_iframe()) {
      $custom_theme = $fb_canvas_data['theme_iframe'];
      $is_canvas = TRUE;
      $use_ob = variable_get(FB_CANVAS_VAR_PROCESS_IFRAME, TRUE);
    }
    if ($is_canvas) {

      // We are serving a canvas page.
      $conf['admin_theme'] = $custom_theme;
      if ($fb_canvas_data['require_login'] == FB_CANVAS_OPTION_REQUIRE_LOGIN) {

        // The application is configured to require login on all canvas pages.
        // However, there are exceptions.
        if (fb_is_profile_tab()) {

          // Redirects are not allowed for the profile tab.
        }
        else {

          // There may be other exceptions, for example some ajax callbacks.  Potential todo item.
          $fb
            ->require_login();
        }
      }

      // Remember the user id.  If fb_user.module changes it, we'll need to refresh the page.  See FB_OP_POST_INIT.
      $original_uid = $user->uid;

      // Hack to init the theme before _drupal_maintenance_theme initializes the wrong one.
      if (variable_get('site_offline', FALSE)) {
        $dummy = theme('dummy');
      }
    }

    // Use buffer for form posts.
    if (!$use_ob && fb_canvas_handling_form()) {
      $use_ob = variable_get(FB_CANVAS_VAR_PROCESS_FBML_FORM, 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 && $_GET['q'] == drupal_get_normal_path(variable_get('site_frontpage', 'node'))) {
      if ($fb
        ->get_loggedin_user()) {
        if ($fb->api_client
          ->users_isAppUser()) {
          $front = $fb_canvas_data['front_added'];
        }
        else {
          $front = $fb_canvas_data['front_loggedin'];
        }
      }
      else {
        $front = $fb_canvas_data['front_anonymous'];
      }
      if ($front) {
        menu_set_active_item(drupal_get_normal_path($front));
      }
    }
  }
  elseif ($op == FB_OP_POST_INIT) {

    //dpm($_REQUEST, "request post init");
    if (isset($original_uid) && $original_uid != $user->uid) {

      // The user has changed, presumably fb_user.module recognized the facebookuser.  We need to refresh canvas pages.
      if (!(arg(0) == 'fb_app' && arg(1) == 'event')) {

        // In order to ensure that drupal handles
        // permissions properly, the user must make the request all over
        // again.  Skip this for the profile tab, as facebook does not allow
        // redirects (or persistent session) there.
        if ((fb_canvas_is_fbml() || fb_canvas_is_iframe()) && !fb_is_profile_tab()) {
          fb_canvas_goto(fb_scrub_urls($_REQUEST['q']));
        }
      }
    }

    // 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'];
    }

    // Give FBML themes a chance to render tabs.
    if (fb_canvas_is_fbml()) {
      _fb_canvas_menu_hack();
    }

    // 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 ($GLOBALS['fb_canvas_post_process']) {
      $output = ob_get_contents();
      ob_end_clean();
      if (fb_canvas_is_fbml()) {
        $output = fb_canvas_process($output, array(
          'force_absolute_canvas' => TRUE,
        ));
      }
      elseif (fb_canvas_is_iframe()) {
        $output = fb_canvas_process($output, array(
          'add_target' => TRUE,
        ));
      }
    }
    if (fb_canvas_handling_form() && fb_canvas_handling_form() !== 'avoid_loop' && $output) {

      // Avoid infinite loop if we get called from drupal_goto.
      fb_canvas_handling_form('avoid_loop');

      // Special handling for forms submitted to Drupal from FBML canvas pages.
      // Cache the results to show the user later.
      $token = uniqid('fb_');
      $cid = session_id() . "_{$token}";
      cache_set($cid, $output, 'cache_page', time() + 60 * 5, drupal_get_headers());

      // (60 * 5) == 5 minutes
      $dest = 'http://apps.facebook.com/' . $fb_app->canvas . "/fb/form_cache/{$cid}";

      // $fb->redirect($dest); // Does not work!
      // Preserve some URL parameters
      $query = array();
      foreach (array(
        'fb_force_mode',
      ) as $key) {
        if ($_REQUEST[$key]) {
          $query[] = $key . '=' . $_REQUEST[$key];
        }
      }

      // drupal_goto honors $_REQUEST['destination'], but we only want
      // that when no errors occurred.
      if (form_get_errors()) {
        unset($_REQUEST['destination']);
        if ($_REQUEST['edit']) {
          unset($_REQUEST['edit']['destination']);
        }
      }
      if (fb_verbose() == 'extreme') {
        watchdog('fb', "Storing cached form page {$cid}, then redirecting to {$dest}, query is " . implode('&', $query));
      }
      drupal_goto($dest, implode('&', $query), NULL, 303);

      // appears to work
    }
    if ((fb_canvas_is_fbml() || 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
            ->redirect($app_destination);
        }
      }
    }
    if (isset($output)) {
      print $output;
    }
  }
}