You are here

fb_app.module in Drupal for Facebook 5.2

Same filename and directory in other branches
  1. 5 fb_app.module
  2. 6.3 fb_app.module
  3. 6.2 fb_app.module
  4. 7.3 fb_app.module

Defines a custom node type that stores a facebook application configuration.

File

fb_app.module
View source
<?php

/**
 * @file
 *   Defines a custom node type that stores a facebook application configuration.
 */
define('FB_APP_REQ_API_KEY', 'fb_sig_api_key');

// Events that a facebook app might need to know about:
define('FB_APP_EVENT_POST_AUTHORIZE', 'post_authorize');
define('FB_APP_EVENT_POST_REMOVE', 'post_remove');
define('FB_APP_PATH_EVENT', 'fb_app/event');
define('FB_APP_OP_EVENT', 'fb_app_event');

/**
 * hook_fb
 */
function fb_app_fb($op, $data, &$return) {
  $fb = $data['fb'];
  $fb_app = $data['fb_app'];
  if ($op == FB_OP_GET_APP) {

    // Load app data, using the criteria passed in
    if ($data['nid']) {
      $fb_app = db_fetch_object(db_query("SELECT * FROM {fb_app} fb INNER JOIN {node} n ON n.nid=fb.nid WHERE fb.nid=%d and status=1", $data['nid']));
    }
    else {
      if ($data['apikey']) {
        $fb_app = db_fetch_object(db_query("SELECT * FROM {fb_app} fb INNER JOIN {node} n ON n.nid=fb.nid WHERE apikey='%s' and status=1", $data['apikey']));
      }
      else {
        if ($data['label']) {
          $fb_app = db_fetch_object(db_query("SELECT * FROM {fb_app} fb INNER JOIN {node} n ON n.nid=fb.nid WHERE label='%s' and status=1", $data['label']));
        }
      }
    }
    if ($fb_app) {
      $return = $fb_app;
    }

    // If we didn't find the app, maybe someone else has written a module that
    // stores app info in a different way.
  }
  else {
    if ($op == FB_OP_GET_ALL_APPS) {

      // Return all known applications
      $result = _fb_app_query_all();
      while ($app = db_fetch_object($result)) {
        $return[] = $app;
      }
    }
    else {
      if ($op == FB_OP_SET_PROPERTIES) {
        $url = url(FB_SETTINGS_APP_NID . '/' . $fb_app->nid . '/' . FB_APP_PATH_EVENT, NULL, NULL, TRUE) . '/';
        $return['uninstall_url'] = $url . FB_APP_EVENT_POST_REMOVE;
        $return['authorize_url'] = $url . FB_APP_EVENT_POST_AUTHORIZE;
      }
      else {
        if ($op == FB_OP_LIST_PROPERTIES) {
          $return[t('Application Name')] = 'application_name';
          $return[t('About URL')] = 'about_url';
          $return[t('Post-Authorize Callback URL')] = 'authorize_url';
          $return[t('Post-Remove Callback URL')] = 'uninstall_url';
        }
      }
    }
  }
}

/**
 * hook_menu
 */
function fb_app_menu($may_cache) {
  $items = array();
  if ($may_cache) {

    // Allow facebook to notify on various events, like adding or removing an app.
    $items[] = array(
      'path' => FB_APP_PATH_EVENT,
      'access' => TRUE,
      'callback' => 'fb_app_event_cb',
      'type' => MENU_CALLBACK,
    );
  }
  return $items;
}

/**
 * Callback for FB_APP_PATH_EVENT.
 * 
 * We don't act on the events directly.  We pass the information along via
 * hook_fb.  Other modules are thus notified of the event and can take action.
 */
function fb_app_event_cb($event_type) {
  fb_invoke(FB_APP_OP_EVENT, array(
    'event_type' => $event_type,
    'fb_app' => $GLOBALS['fb_app'],
    'fb' => $GLOBALS['fb'],
  ));

  // This page is called by facebook, not a user's browser.
  print 'Thanks Facebook, for your fancy API!';

  // Problems if we save session during Facebook Connect, because the event callback's share session with normal pages.
  session_save_session(FALSE);
  exit;
}

/**
 * hook_node_info.
 */
function fb_app_node_info() {
  return array(
    'fb_app' => array(
      'name' => t('Facebook Application'),
      'module' => 'fb_app',
      'description' => t('Information such as apikey and secret for a facebook.com application.'),
      'help' => t('Configure the behavior of your Facebook Application.'),
    ),
  );
}
function fb_app_access($op, $node) {
  global $user;
  if (user_access('administer fb apps')) {
    return TRUE;
  }
  if ($op == 'create' && user_access('create fb apps')) {
    return TRUE;
  }
  else {
    if ($op == 'update' || $op == 'delete') {
      if ($node->uid == $user->uid && user_access('edit own fb apps')) {
        return TRUE;
      }
    }
  }
}
function fb_app_perm() {
  return array(
    'administer fb apps',
    'create fb apps',
    'edit own fb apps',
  );
}
function fb_app_form(&$node, &$param) {

  // Helpful link
  if (!$node->nid) {
    drupal_set_message(t("Before completing this form, <a href=!url>create your application</a> on Facebook.", array(
      '!url' => 'http://www.facebook.com/developers/editapp.php?new',
    )));
  }
  $form = array();
  $type = node_get_types('type', $node);

  // We need to define form elements for the node's title and body.
  $form['title'] = array(
    '#type' => 'textfield',
    '#title' => check_plain($type->title_label),
    '#required' => TRUE,
    '#default_value' => $node->title,
    '#weight' => -5,
    '#description' => t('Identifies the application to site administrators.'),
  );

  // We want the body and filter elements to be adjacent. We could try doing
  // this by setting their weights, but another module might add elements to the
  // form with the same weights and end up between ours. By putting them into a
  // sub-array together, we're able force them to be rendered together.
  $form['body_filter']['body'] = array(
    '#type' => 'textarea',
    '#title' => check_plain($type->body_label),
    '#default_value' => $node->body,
    '#required' => FALSE,
    '#description' => 'A brief description of the application.',
  );
  $form['body_filter']['filter'] = filter_form($node->format);

  // Now we define the form elements specific to our node type.
  $form['fb_app'] = array(
    '#tree' => TRUE,
    '#weight' => -4,
    '#validate' => array(
      'fb_app_validate_fb_app' => array(),
    ),
  );
  $form['fb_app']['label'] = array(
    '#type' => 'textfield',
    '#title' => t('Label'),
    '#required' => TRUE,
    '#default_value' => $node->fb_app->label,
    '#description' => t('Used behind the scenes, for naming roles, styles, etc.  Use no spaces or weird characters.<br/>Working with multiple copies of a site (i.e. development, staging, production)?  Use the <strong>same</strong> label for each.  Node id and apikey will change from server to server, but the label remains the same.'),
  );
  $form['fb_app']['apikey'] = array(
    '#type' => 'textfield',
    '#title' => t('API Key'),
    '#required' => TRUE,
    '#default_value' => $node->fb_app->apikey,
    '#description' => t('Facebook will generate this value when you create the application.'),
  );
  $form['fb_app']['secret'] = array(
    '#type' => 'textfield',
    '#title' => t('Secret'),
    '#required' => TRUE,
    '#default_value' => $node->fb_app->secret,
    '#description' => t('Facebook will generate this value when you create the application.'),
  );
  $form['fb_app']['id'] = array(
    '#type' => 'textfield',
    '#title' => t('Facebook App ID'),
    '#required' => FALSE,
    '#default_value' => $node->fb_app->id,
    '#description' => t('To learn this number, visit your app\'s About Page (or other links from Facebook\'s Developer App).  The URL will end in ?id=1234...  Enter the number that follows "?id=" here.'),
  );

  // fb_app_data is a placeholder to make it easier for other module to attach
  // various settings to the node.
  $form['fb_app_data'] = array(
    '#tree' => TRUE,
  );

  // Add our own fields to fb_app_data.  Other modules use hook_form_alter to do this.
  $fb_app_data = fb_app_get_data($node->fb_app);
  $data = $fb_app_data['fb_app'];
  if (!$data) {
    $data = array(
      'set_app_props' => TRUE,
    );
  }

  // defaults
  $form['fb_app_data']['fb_app']['set_app_props'] = array(
    '#type' => 'checkbox',
    '#title' => t('Set Application Properties Automatically'),
    '#description' => t('Automatically update Facebook settings for this application.  Disable to prevent customized settings from being overwritten.'),
    '#default_value' => $data['set_app_props'],
  );
  return $form;
}
function fb_app_validate($node) {

  // TODO: check label is unique, and role name will be unique.
  // check apikey is unique, canvas page is unique
  $fb_app = (object) $node->fb_app;
  fb_app_get_app_properties($fb_app);
  if (!$fb_app->application_name) {
    form_set_error('fb_app][apikey', t('Unable to get application properties.  Confirm both apikey and secret.'));
  }
}
function fb_app_validate_fb_app() {

  //dpm(func_get_args(), 'fb_app_validate_fb_app');
}
function fb_app_load($node) {
  $fb_app = db_fetch_object(db_query('SELECT * FROM {fb_app} WHERE nid=%d', $node->nid));
  $fb_app_data = fb_app_get_data($fb_app);
  return array(
    'fb_app' => $fb_app,
    'fb_app_data' => $fb_app_data,
  );
}
function fb_app_view($node, $teaser = FALSE, $page = FALSE) {
  $node = node_prepare($node, $teaser);
  $fb_app = (object) $node->fb_app;

  // cast in case of preview
  fb_app_get_app_properties($fb_app);

  // Perhaps this info should be hidden, unless user can edit node.
  if (user_access('administer fb apps')) {
    if ($fb_app->edit_url) {
      $node->content['edit_link'] = array(
        '#value' => '<p>' . t('<a href="!url">Edit this application</a> on Facebook.', array(
          '!url' => $fb_app->edit_url,
        )) . '</p>',
        '#type' => 'markup',
      );
    }
    $node->content['fb_app'] = array(
      '#value' => theme('fb_app', $fb_app),
      '#weight' => 1,
    );
  }
  return $node;
}
function fb_app_get_about_url($fb_app) {
  if ($fb_app->id) {
    return url("http://www.facebook.com/apps/application.php", "id={$fb_app->id}");
  }
}
function theme_fb_app($fb_app) {

  // Get known properties
  $props_map = fb_invoke(FB_OP_LIST_PROPERTIES, array(
    'fb_app' => $fb_app,
  ), array());
  $data = array(
    t('Label') => $fb_app->label,
    t('API Key') => $fb_app->apikey,
    t('Secret') => $fb_app->secret,
  );
  foreach ($props_map as $name => $key) {
    if ($fb_app->{$key}) {
      $data[$name] = $fb_app->{$key};
    }
  }
  $output .= theme('dl', $data);
  return $output;
}

// this belongs elsewhere
function theme_dl($items) {
  if (count($items)) {
    $output = "<dl>\n";
    foreach ($items as $term => $data) {
      $output .= "  <dt>{$term}</dt><dd>{$data}</dd>\n";
    }
    $output .= "</dl>\n";
    return $output;
  }
}

/**
 * Get properties from Facebook.  Fills in the data that we need to
 * know by querying facebook.
 */
function fb_app_get_app_properties(&$fb_app) {
  static $cache;
  static $props_map;
  if (!isset($cache)) {
    $cache = array();

    // http://wiki.developers.facebook.com/index.php/ApplicationProperties
    $props_map = array(
      t('About URL') => 'about_url',
      t('Application Name') => 'application_name',
      t('Edit URL') => 'edit_url',
    );
    $props_map = fb_invoke(FB_OP_LIST_PROPERTIES, array(
      'fb_app' => $fb_app,
    ), $props_map);
  }
  if (!isset($cache[$fb_app->apikey])) {
    if ($fb = fb_api_init($fb_app, FB_FBU_NO_SESSION)) {
      try {
        $props = $fb->api_client
          ->admin_getAppProperties(array_values($props_map));
      } catch (Exception $e) {
        fb_log_exception($e, t('Failed to get application properties from Facebook'));
      }
      $cache[$fb_app->apikey] = $props;
    }
  }
  else {
    $props = $cache[$fb_app->apikey];
  }

  // Update $fb_app with the values we got from facebook api.
  foreach ($props_map as $key) {
    if ($props[$key]) {
      $fb_app->{$key} = $props[$key];
    }
  }
}
function fb_app_set_app_properties($fb_app) {
  $fb_app_data = fb_app_get_data($fb_app);
  if ($fb_app_data['fb_app']['set_app_props']) {
    $props = fb_invoke(FB_OP_SET_PROPERTIES, array(
      'fb_app' => $fb_app,
    ), array());
    if (count($props)) {
      if ($fb = fb_api_init($fb_app, FB_FBU_NO_SESSION)) {
        try {
          $fb->api_client
            ->admin_setAppProperties($props);
        } catch (Exception $e) {
          fb_log_exception($e, t('Failed to set application properties on Facebook'));
        }
      }
    }
  }
}
function fb_app_insert($node) {

  //drupal_set_message("fb_app_insert" . dpr($node,1));
  $fb_app = (object) $node->fb_app;
  fb_app_get_app_properties($fb_app);

  // Get canvas and possible other props
  $fb_app->data = serialize($node->fb_app_data);
  $fb_app->nid = $node->nid;
  db_query("INSERT INTO {fb_app} (nid, label, apikey, secret, id, canvas, require_login, create_account, unique_account, data) VALUES (%d, '%s', '%s', '%s', '%s', '%s', %d, %d, %d, '%s')", $node->nid, $fb_app->label, $fb_app->apikey, $fb_app->secret, $fb_app->id, $fb_app->canvas_name, $fb_app->require_login, $fb_app->create_account, $fb_app->unique_account, $fb_app->data);
  watchdog('fb_app', t('Created Facebook Application %label.', array(
    '%label' => $fb_app->label,
  )), WATCHDOG_NOTICE, l($node->title, 'node/' . $node->nid));
  fb_app_set_app_properties($fb_app);

  // Set callback URL, etc.
}
function fb_app_update($node) {
  $fb_app = (object) $node->fb_app;
  fb_app_get_app_properties($fb_app);

  // Get canvas and possible other props
  $fb_app->data = serialize($node->fb_app_data);
  $fb_app->nid = $node->nid;
  db_query("UPDATE {fb_app} SET label='%s', apikey='%s', secret='%s', id='%s', canvas='%s', require_login=%d, create_account=%d, unique_account=%d, data='%s' WHERE nid=%d", $fb_app->label, $fb_app->apikey, $fb_app->secret, $fb_app->id, $fb_app->canvas_name, $fb_app->require_login, $fb_app->create_account, $fb_app->unique_account, $fb_app->data, $node->nid);
  fb_app_set_app_properties($fb_app);

  // Set callback URL, etc.
}
function fb_app_delete($node) {
  db_query('DELETE FROM {fb_app} WHERE nid=%d', $node->nid);

  // Should we unset properties on facebook?
}

/**
 * Convenience method for other modules to attach data to the fb_app object.
 */
function fb_app_get_data(&$fb_app) {
  if (!$fb_app->fb_app_data) {
    $fb_app->fb_app_data = unserialize($fb_app->data);
  }
  return $fb_app->fb_app_data;
}

/**
 * Helper function returns a database query for all apps.
 */
function _fb_app_query_all() {
  $result = db_query("SELECT fb.*, n.title FROM {fb_app} fb INNER JOIN {node} n ON n.nid=fb.nid WHERE status=1");
  return $result;
}
function _fb_app_query_by_label($label) {
  $result = db_query("SELECT fb.*, n.title FROM {fb_app} fb INNER JOIN {node} n ON n.nid=fb.nid WHERE n.status=1 AND fb.label = '%s'", $label);
  return $result;
}

/**
 * Implementation of hook_user.
 */
function fb_app_user($op, &$edit, &$account, $category = NULL) {
  $items = array();
  if ($op == 'view') {
    $result = _fb_app_query_all();
    while ($fb_app = db_fetch_object($result)) {

      // Learn this user's FB id
      $fbu = fb_get_fbu($account->uid, $fb_app);
      if ($fbu) {

        // The drupal user is a facebook user.  Now, learn more from facebook.
        $fb = fb_api_init($fb_app, FB_FBU_ANY);
        if (fb_facebook_user($fb)) {
          try {
            $info = $fb->api_client
              ->users_getInfo(array(
              $fbu,
            ), array(
              'about_me',
              'affiliations',
              'name',
              'is_app_user',
              'pic_big',
              'profile_update_time',
              'status',
            ));
          } catch (Exception $e) {
            fb_log_exception($e, "Failed to get Facebook user info for account {$fbu}");
          }
        }
        if (count($info)) {
          $output = theme('fb_app_user_info', $fb_app, $info[0]);
          $items[$fb_app->label] = array(
            'title' => $fb_app->title,
            'value' => $output,
            'class' => 'fb_app',
          );
        }
        else {
          fb_report_errors($fb);
        }
      }
    }
    if (count($items)) {
      return array(
        t('Facebook') => $items,
      );
    }
  }
}
function theme_fb_app_user_info($fb_app, $info) {
  if ($info['pic_big']) {
    $output .= '<p><img src="' . $info['pic_big'] . '" /></p>';
  }
  $fb_link = l($info['name'], 'http://www.facebook.com/profile.php', NULL, 'id=' . $info['uid']);
  if ($info['is_app_user']) {
    $output .= '<p>' . t('!fb_link uses %title', array(
      '!fb_link' => $fb_link,
      '%title' => $fb_app->title,
    )) . '</p>';
  }
  else {
    $output .= '<p>' . t('!fb_link does not use %title', array(
      '!fb_link' => $fb_link,
      '%title' => $fb_app->title,
    )) . '</p>';
  }
  return $output;
}
function fb_app_token_list($type = 'all') {
  if ($type == 'all' || $type == 'fb' || $type == 'fb_app') {
    $tokens['fb_app']['fb-app-nid'] = t('Facebook application ID');
    $tokens['fb_app']['fb-app-title'] = t('Facebook application title');
    $tokens['fb_app']['fb-app-url'] = t('Facebook application URL (base path)');
  }
  return $tokens;
}
function fb_app_token_values($type = 'all', $object = NULL) {
  if ($type == 'fb_app' && $object) {
    $fb_app = $object;
    $values['fb-app-title'] = $fb_app->title;
    $values['fb-app-nid'] = $fb_app->nid;
    $values['fb-app-url'] = 'http://apps.facebook.com/' . $fb_app->canvas;
  }
  return $values;
}

Functions

Namesort descending Description
fb_app_access
fb_app_delete
fb_app_event_cb Callback for FB_APP_PATH_EVENT.
fb_app_fb hook_fb
fb_app_form
fb_app_get_about_url
fb_app_get_app_properties Get properties from Facebook. Fills in the data that we need to know by querying facebook.
fb_app_get_data Convenience method for other modules to attach data to the fb_app object.
fb_app_insert
fb_app_load
fb_app_menu hook_menu
fb_app_node_info hook_node_info.
fb_app_perm
fb_app_set_app_properties
fb_app_token_list
fb_app_token_values
fb_app_update
fb_app_user Implementation of hook_user.
fb_app_validate
fb_app_validate_fb_app
fb_app_view
theme_dl
theme_fb_app
theme_fb_app_user_info
_fb_app_query_all Helper function returns a database query for all apps.
_fb_app_query_by_label

Constants

Namesort descending Description
FB_APP_EVENT_POST_AUTHORIZE
FB_APP_EVENT_POST_REMOVE
FB_APP_OP_EVENT
FB_APP_PATH_EVENT
FB_APP_REQ_API_KEY @file Defines a custom node type that stores a facebook application configuration.