label, $op)" . dpr($fb_app, 1)); if ($op == FB_OP_GET_APP) { // This operation determines which app the request is for. Theoretically, // the allows fb_app and other app-defining modules to co-exist. We need // to determine if the app is one of ours and if so, return the app // details. // If apikey or nid was passed to us, use that 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 ($apikey = $_REQUEST[FB_APP_REQ_API_KEY]) { // If facebook has passed the app key, let's use that. $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", $apikey)); } else { if (function_exists('fb_settings')) { // See settings.inc if ($nid = fb_settings(FB_SETTINGS_APP_NID)) { // Here if we're in iframe, using our /fb_canvas/nid/ path convention. $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", $nid)); } } } 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_INITIALIZE) { // User init has been moved to fb_user.module. // For now, nothing to do here. } else if ($op == FB_OP_POST_INIT) { /* TODO: move this feature to an explicit setting. For now, we just use site homepage // Here we override the front_page settings if ($_REQUEST['q'] == '' && $fb_app->nid) { menu_set_active_item('node/'.$fb_app->nid); // Note, menu_set_active_item only works as intended if fb.module // is weighted lighter than node.module! } */ } } /** * 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) { 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, create your application 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' => 'Not sure yet how this will be used.', ); $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, ); $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.'), ); $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']['canvas'] = array('#type' => 'textfield', '#title' => t('Canvas Page Url Suffix'), '#required' => TRUE, '#default_value' => $node->fb_app->canvas, '#description' => t('Type only the part that comes after "http://apps.facebook.com/"'), ); $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); // TODO: move this to another module, or get rid of it entirely. /* XXX This needs to be re-thought. Disabling for now. $form['fb_app_blocks'] = array('#tree' => TRUE); for ($i = 0; $i < _fb_app_num_blocks(); $i++) { $form['fb_app_blocks'][$i]['body'] = array('#type' => 'textarea', '#title' => t('Block !num', array('!num' => $i+1)), '#default_value' => $node->fb_app_blocks[$i]->body, '#required' => FALSE, '#description' => t('Enter markup to be used as this application\'s navigation. Typically this is an unordered list of links.'), ); $form['fb_app_blocks'][$i]['format'] = filter_form($node->fb_app_blocks[$i]->format, NULL, array('fb_app_blocks', $i, 'format')); } */ 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 // check no menu items start with $fb_app->canvas, because we will rewrite those URLs } 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); $result = db_query('SELECT * FROM {fb_app_block} WHERE nid=%d', $node->nid); while ($data = db_fetch_object($result)) $blocks[$data->delta] = $data; return array('fb_app' => $fb_app, 'fb_app_blocks' => $blocks, 'fb_app_data' => $fb_app_data, ); } function fb_app_view($node, $teaser=FALSE, $page=FALSE) { $node = node_prepare($node, $teaser); // Perhaps this info should be hidden, unless user can edit node. if (user_access('administer fb apps')) { $node->content['fb_app'] = array('#value' => theme('fb_app', $node->fb_app), '#weight' => 1); if (count($node->fb_app_blocks)) foreach($node->fb_app_blocks as $delta => $data) { $node->content['fb_app_blocks'][$delta] = array('subject' => array('#value' => t('Block %num', array('%num' => $delta+1))), 'content' => array('#value' => check_markup($data->body, $data->format, FALSE), '#weight' => 1)); } $node->content['fb_app_blocks']['#weight'] = 10; } return $node; } function theme_fb_app($data) { return theme('dl', array(t('Label') => $data->label, t('API Key') => $data->apikey, t('Secret') => $data->secret, t('About page') => $data->id ? url("http://www.facebook.com/apps/application.php", "id=$data->id") : t('N/A'), // TODO: edit and logout URLs )); } // this belongs elsewhere function theme_dl($items) { if (count($items)) { $output = "
\n"; foreach ($items as $term => $data) { $output .= "
$term
$data
\n"; } $output .= "
\n"; return $output; } } function fb_app_insert($node) { //drupal_set_message("fb_app_insert" . dpr($node,1)); $fb_app = (object) $node->fb_app; // Create a new role for users of this account // Might be more clean to programatically fill out the add role form $role_name = $fb_app->label . ' user'; db_query("INSERT INTO {role} (name) VALUES ('%s')", $role_name); $role = db_fetch_object(db_query("SELECT * FROM {role} WHERE name='%s'", $role_name)); $data = serialize($node->fb_app_data); db_query("INSERT INTO {fb_app} (nid, label, apikey, secret, id, canvas, require_login, create_account, unique_account, rid, data) VALUES (%d, '%s', '%s', '%s', '%s', '%s', %d, %d, %d, %d, '%s')", $node->nid, $fb_app->label, $fb_app->apikey, $fb_app->secret, $fb_app->id, $fb_app->canvas, $fb_app->require_login, $fb_app->create_account, $fb_app->unique_account, $role->rid, $data ); _fb_app_blocks_update($node); watchdog('fb_app', t('Created Facebook Application %label. Created role %role.', array('%label' => $fb_app->label, '%role' => $role_name)), WATCHDOG_NOTICE, l($node->title, 'node/'.$node->nid)); } function _fb_app_blocks_update($node) { db_query('DELETE FROM {fb_app_block} WHERE nid=%d', $node->nid); if (count($node->fb_app_blocks)) foreach ($node->fb_app_blocks as $delta => $data_array) { $data = (object)$data_array; //if (!$delta) // $delta = '0'; db_query("INSERT INTO {fb_app_block} (nid, delta, body, format) VALUES (%d, '%s', '%s', %d)", $node->nid, $delta, $data->body, $data->format); } } function fb_app_update($node) { $fb_app = (object) $node->fb_app; $data = serialize($node->fb_app_data); // TODO: test if role name needs to change 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, $fb_app->require_login, $fb_app->create_account, $fb_app->unique_account, $data, $node->nid); _fb_app_blocks_update($node); } function fb_app_delete($node) { // TODO: clean up users, permissions db_query('DELETE FROM {role} WHERE rid=%d', $node->fb_app->rid); db_query('DELETE FROM {fb_app} WHERE nid=%d', $node->nid); db_query('DELETE FROM {fb_app_block} WHERE nid=%d', $node->nid); } /** * 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; } function _fb_app_num_blocks() { return variable_get('fb_app_num_blocks', 1); } function fb_app_block($op = 'list', $delta = 0, $edit = array()) { if ($op == 'list') { for ($i = 0; $i < _fb_app_num_blocks(); $i++) $items[$i]['info'] = t('Facebook Application block !num', array('!num' => $i+1)); return $items; } else if ($op == 'view') { global $fb_app; $result = db_query("SELECT * FROM {fb_app_block} WHERE nid=%d AND delta='%s'", $fb_app->nid, $delta); $data = db_fetch_object($result); if ($data) { $output = check_markup($data->body, $data->format, FALSE); return array('content' => $output, // TODO: subject ); } } } /** * 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)) { $info = $fb->api_client->users_getInfo(array($fbu), array('about_me', 'affiliations', 'name', 'is_app_user', 'pic_big', 'profile_update_time', 'status', )); } 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 .= '

'; $fb_link = l($info['name'], 'http://www.facebook.com/profile.php', NULL, 'id='.$info['uid']); if ($info['is_app_user']) $output .= '

' . t('!fb_link uses %title', array('!fb_link' => $fb_link, '%title' => $fb_app->title)) . '

'; else $output .= '

'. t('!fb_link does not use %title', array('!fb_link' => $fb_link, '%title' => $fb_app->title)) . '

'; 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; } ?>