'fb_feed/register', 'title' => t('Register template'), 'type' => MENU_CALLBACK, 'access' => user_access('administer fb apps'), 'callback' => 'fb_feed_register_cb', ); $items[] = array('path' => 'fb_feed/deactivate', 'title' => t('Deactivate template'), 'type' => MENU_CALLBACK, 'access' => user_access('administer fb apps'), 'callback' => 'drupal_get_form', 'callback arguments' => array('fb_feed_deactivate_confirm'), ); } else { if (arg(0) == 'node' && is_numeric(arg(1))) { $node = node_load(arg(1)); if ($node->type == 'fb_app') { $fb_app_data = fb_app_get_data($node->fb_app); $items[] = array('path' => "node/$node->nid/fb_feed/templates", 'title' => t('Feed Templates'), 'type' => MENU_LOCAL_TASK, 'access' => node_access('update', $node), 'callback' => 'fb_feed_template_page', 'callback arguments' => array($node->nid), ); } } } return $items; } function fb_feed_deactivate_confirm($fb_app_nid, $bundle_id) { $result = db_query("SELECT n.nid, n.title FROM {node} n LEFT JOIN {fb_feed_template} fft ON fft.nid = n.nid WHERE fft.fb_app_nid = %d AND fft.bundle_id = %f", $fb_app_nid, $bundle_id); $data = db_fetch_object($result); if ($data->nid) { $form['nid'] = array('#type' => 'hidden', '#value' => $data->nid); $description = t('This will deactivate the template on Facebook. The template node will not be deleted.'); $question = t('Really deactivate the !template_link template?', array('!template_link' => l($data->title, 'node/'.$data->nid))); } else $question = t('Really deactivate template bundle %bundle_id?', array('%bundle_id' => $bundle_id)); $form['bundle_id'] = array('#type' => 'hidden', '#value' => $bundle_id); $form['fb_app_nid'] = array('#type' => 'hidden', '#value' => $fb_app_nid); return confirm_form($form, $question, 'node/'.$fb_app_nid.'/fb_feed/templates', $description); } function fb_feed_deactivate_confirm_submit($form_id, $values) { $success = fb_feed_deactivate_template($values['fb_app_nid'], $values['bundle_id']); if ($success && $values['nid']) db_query("UPDATE {fb_feed_template} SET bundle_id=0 WHERE nid=%d", $values['nid']); if ($success) { drupal_set_message(t('The template %bundle_id has been deactivated.', array('%bundle_id' => $values['bundle_id']))); } else { drupal_set_message(t('The template %bundle_id could not be deactivated.', array('%bundle_id' => $values['bundle_id'])), 'error'); } return 'node/'.$values['fb_app_nid'].'/fb_feed/templates'; } function fb_feed_deactivate_template($fb_app_nid, $bundle_id) { $fb_app = fb_get_app(array('nid' => $fb_app_nid)); $fb = fb_api_init($fb_app, FB_FBU_ANY); if (!$fb) { drupal_set_message(t('Facebook API failed to connect. Template could not be deactivated.', 'error')); return FALSE; } else { $result = $fb->api_client->feed_deactivateTemplateBundleByID($bundle_id); fb_report_errors($fb, 'Error while deactivating template.'); } return $result; } function fb_feed_register_cb($nid) { $node = node_load($nid); $bundle_id = fb_feed_register_template($node); drupal_goto('node/'.$node->fb_app_nid.'/fb_feed/templates'); } function fb_feed_template_page($fb_app_nid) { $registered = array(); $unregistered = array(); // Query all known templates $result = db_query("SELECT n.nid, n.title, n.status, fft.bundle_id FROM {node} n LEFT JOIN {fb_feed_template} fft ON fft.nid = n.nid WHERE fft.nid IS NOT NULL"); while ($data = db_fetch_object($result)) { if ($data->bundle_id) { $registered[$data->bundle_id] = $data; } else { $unregistered[] = $data; } } // Query facebook for a list of registered templates we know nothing about. $fb_app = fb_get_app(array('nid' => $fb_app_nid)); $fb = fb_api_init($fb_app, FB_FBU_ANY); if ($fb) { try { $all = $fb->api_client->feed_getRegisteredTemplateBundles(); if (is_array($all)) foreach ($all as $data) { if (isset($registered[$data['template_bundle_id']])) { $registered[$data['template_bundle_id']]->confirmed = $data; } else $registered[$data['template_bundle_id']] = $data; } } catch (Exception $e) { fb_log_exception($e, t('Failed to get template bundles.')); drupal_set_message(t('This page may show more information if you configure an infinite session or Facebook Connect.')); } } else { drupal_set_message("Facebook API not available. Some options will not be shown. Possible infinite session configuration problem.", 'error'); } // Display one table of registered templates and another of unregistered. if (count($unregistered)) { $rows = array(); foreach ($unregistered as $data) { $ops = array(l(t('register'), 'fb_feed/register/'.$data->nid)); $row = array(l($data->title, 'node/'.$data->nid), implode(' | ', $ops)); $rows[] = $row; } $header = array(t('Unregistered Template'), t('Operations')); $output .= theme('table', $header, $rows); } if (count($registered)) { $rows = array(); foreach ($registered as $data) { $ops = array(); if ($data->confirmed) $ops[] = l(t('deactivate'), 'fb_feed/deactivate/'.$fb_app->nid.'/'.$data->bundle_id); else if ($data->nid && $fb && is_array($all)) $ops = array(l(t('re-register'), 'fb_feed/register/'.$data->nid)); else if ($data->template_bundle_id) $ops[] = l(t('deactivate'), 'fb_feed/deactivate/'.$fb_app->nid.'/'.$data['template_bundle_id']); if ($data->nid) $title = l($data->title, 'node/'.$data->nid); else // Use a template learned from facebook. $title = $data['one_line_story_templates'][0]; if ($data->bundle_id) $bundle_id = $data->bundle_id; else $bundle_id = $data['template_bundle_id']; $row = array($title, $bundle_id, implode(' | ', $ops), ); $rows[] = $row; } $header = array(t('Registered Template'), t('Bundle ID'), t('Operations')); $output .= theme('table', $header, $rows); } return $output; } /** * hook_node_info. */ function fb_feed_node_info() { return array(FB_FEED_NODE_TYPE_TEMPLATE => array('name' => t('Facebook Template Bundle'), 'module' => 'fb_feed', 'description' => t('Templates for posting information to Facebook feeds.'), 'help' => t('Configure the feed stories of your Facebook Application.'), ), ); } /** * Use same permissions as fb_app module. */ function fb_feed_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_feed_form(&$node, &$param) { $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 template bundle to site administrators.'), ); if ($type->body_label) { $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. $options = fb_get_app_options(FALSE); $form['fb_app_nid'] = array('#type' => 'select', '#title' => t('Application'), '#default_value' => $values['fb_app_nid'], '#options' => $options, '#description' => t('Which application will use these templates?
'), '#weight' => -5, '#required' => TRUE, ); $form['fb_feed_data'] = array('#tree' => TRUE, '#weight' => -4, ); $form['fb_feed_data']['description'] = array('#type' => 'markup', '#value' => t('Read about template bundles for more information.', array('!url' => 'http://wiki.developers.facebook.com/index.php/Feed.registerTemplateBundle')), ); $form['fb_feed_data']['example'] = array('#type' => 'markup', '#value' => t('Here\'s an example that produces a message like "User Name wrote about something on application," where something is the title of a node:
{*actor*} wrote about {*title*} on {*fb-app-title*}'), ); $form['fb_feed_data']['line'] = array('#type' => 'fieldset', '#title' => t('One-line templates'), '#description' => t('Note that all one-line templates must begin with the {*actor*} token.'), '#collapsible' => TRUE, ); $i = 0; while ($i < FB_FEED_LINES_PER_BUNDLE) { $form['fb_feed_data']['line'][$i] = array('#type' => 'textfield', '#title' => t('Additional one-line template'), '#default_value' => $node->fb_feed_data['line'][$i], ); $i++; } $form['fb_feed_data']['line'][0]['#title'] = t('Primary one-line template'); $form['fb_feed_data']['line'][0]['#required'] = TRUE; $form['fb_feed_data']['short'] = array('#type' => 'fieldset', '#title' => t('Short templates'), '#description' => t('Note that each title must begin with the {*actor*} token.'), '#collapsible' => TRUE, ); $i = 0; while ($i < FB_FEED_SHORTS_PER_BUNDLE) { $form['fb_feed_data']['short'][$i]['template_title'] = array('#type' => 'textfield', '#title' => t('Additional short template title'), '#default_value' => $node->fb_feed_data['short'][$i]['template_title'], ); $form['fb_feed_data']['short'][$i]['template_body'] = array('#type' => 'textarea', '#title' => t('Additional short template body'), '#default_value' => $node->fb_feed_data['short'][$i]['template_body'], '#rows' => 2, ); $i++; } $form['fb_feed_data']['short'][0]['template_title']['#title'] = t('Primary short template title'); $form['fb_feed_data']['short'][0]['template_body']['#title'] = t('Primary short template body'); $form['fb_feed_data']['full'] = array('#type' => 'fieldset', '#title' => t('Full template'), '#description' => t(''), '#collapsible' => TRUE, ); $form['fb_feed_data']['full']['template_title'] = array('#type' => 'textfield', '#title' => t('Full template title'), '#default_value' => $node->fb_feed_data['full']['template_title'], ); $form['fb_feed_data']['full']['template_body'] = array('#type' => 'textarea', '#title' => t('Full template body'), '#default_value' => $node->fb_feed_data['full']['template_body'], '#rows' => 8, ); // TODO: If token module enabled $form['token_help'] = array('#type' => 'fieldset', '#collapsible' => TRUE, '#collapsed' => FALSE, '#title' => t("Token help"), '#description' => t('Facebook uses the special token {*actor*} for the user performing an action, and {*target*} when the action applies to one (or more) of actor\'s friends. Otherwise, It is recommended to name your tokens as you would with the token module. Remember that in Facebook templates you use {*token*} syntax.'), ); foreach (array('node', 'comment', 'user', 'fb_app') as $type) { $form['token_help'][$type] = array('#type' => 'fieldset', '#collapsible' => TRUE, '#collapsed' => TRUE, '#title' => t("!type tokens", array('!type' => $type)), '#description' => theme('token_help', $type, "{*", "*}"), ); } return $form; } function fb_feed_validate($node) { // TODO } function fb_feed_register_template($node) { $fb_app = fb_get_app(array('nid' => $node->fb_app_nid)); // Login to facebook as either the current user or the infinite session if ($fb_app) { $fb = fb_api_init($fb_app, FB_FBU_ANY); if ($fb) { // Clean up the data before sending to facebook. Send no empty templates. $lines = array(); foreach ($node->fb_feed_data['line'] as $line) { if ($line) $lines[] = $line; } $shorts = array(); foreach ($node->fb_feed_data['short'] as $item) { if ($item['template_title']) $shorts[] = $item; } $full = array(); if ($node->fb_feed_data['full']['template_title']) $full = $node->fb_feed_data['full']; if ($fb) { try { $bundle_id = $fb->api_client->feed_registerTemplateBundle($lines, $shorts, $full); } catch (Exception $e) { fb_log_exception($e, t('Error attempting to register template bundle.'), $fb); } if ($bundle_id) drupal_set_message(t('Registered template bundle %bundle_id with Facebook.', array('%bundle_id' => $bundle_id))); else drupal_set_message(t('Failed to register the template.'), 'error'); if ($bundle_id && $node->nid) db_query("UPDATE {fb_feed_template} fft SET bundle_id=%f WHERE nid=%d", $bundle_id, $node->nid); return $bundle_id; } } } // TODO: report failure to connect to facebook api drupal_set_message(t('Failed to connect to Facebook for application %app.', array('%app' => $fb_app->title)), 'error'); } function fb_feed_insert($node) { // Register the new template with facebook. $bundle_id = fb_feed_register_template($node); if (!$bundle_id) // Show an error, but save the node anyway. drupal_set_message(t('Failed to register template bundle with Facebook. This template bundle may not be used.'), 'error'); $data = serialize($node->fb_feed_data); // We're going to save the apikey, although it may be redundant with the // fb_app_nid, so that we can detect any inconsistencies (i.e. the apikey // has changed on us). $fb_app = fb_get_app(array('nid' => $node->fb_app_nid)); db_query("INSERT INTO {fb_feed_template} (nid, fb_app_nid, apikey, bundle_id, fb_feed_data) VALUES (%d, %d, '%s', %f, '%s')", $node->nid, $node->fb_app_nid, $fb_app->apikey, $bundle_id, $data); if ($bundle_id) watchdog('fb_feed', t('Registered Facebook feed template bundle %id for application %app', array('%id' => $bundle_id, '%app' => $fb_app->title, ))); } function fb_feed_update($node) { //dpm($node, "fb_feed_update"); // deactivate the previously registered bundle if ($node->bundle_id) { fb_feed_deactivate_template($node->fb_app_nid, $node->bundle_id); unset($node->bundle_id); } // Register the new template with facebook. $bundle_id = fb_feed_register_template($node); if (!$bundle_id) // Show an error, but save the node anyway. drupal_set_message(t('Failed to register template bundle with Facebook. This template bundle may not be used.'), 'error'); $data = serialize($node->fb_feed_data); // We're going to save the apikey, although it may be redundant with the // fb_app_nid, so that we can detect any inconsistencies (i.e. the apikey // has changed on us). $fb_app = fb_get_app(array('nid' => $node->fb_app_nid)); db_query("UPDATE {fb_feed_template} SET fb_app_nid=%d, apikey='%s', bundle_id=%f, fb_feed_data='%s' WHERE nid=%d", $node->fb_app_nid, $fb_app->apikey, $bundle_id, $data, $node->nid); if ($bundle_id) watchdog('fb_feed', t('Updated Facebook feed template bundle %id for application %app', array('%id' => $bundle_id, '%app' => $fb_app->title, ))); } function fb_feed_delete($node) { // TODO: deactivate the previously registered bundle db_query('DELETE FROM {fb_feed_template} WHERE nid=%d', $node->nid); } function fb_feed_load($node) { $data = db_fetch_array(db_query('SELECT fb_app_nid, apikey, bundle_id, fb_feed_data FROM {fb_feed_template} WHERE nid=%d', $node->nid)); $data['fb_feed_data'] = unserialize($data['fb_feed_data']); return $data; } // TODO: hook_nodeapi. React appropriately if fb_app apikey changes or is deleted. function fb_feed_view($node, $teaser=FALSE, $page=FALSE) { $node = node_prepare($node, $teaser); $fb_app = fb_get_app(array('nid' => $node->fb_app_nid)); if (!$node->bundle_id) drupal_set_message(t('The template shown must be registered with Facebook before it can be used.'), 'error'); $items = array(t('Application') => l($fb_app->title, 'node/'.$fb_app->nid), t('Template Bundle ID') => $node->bundle_id ? $node->bundle_id : ''.t('Template bundle not registered') . '', ); $items[t('One-line templates')] = implode("
\n", $node->fb_feed_data['line']); foreach ($node->fb_feed_data['short'] as $key => $template) { if (is_array($template)) $items[t('Short template %num', array('%num' => $key +1))] = implode("
\n", $node->fb_feed_data['short'][$key]); } if ($node->fb_feed_data['full']['template_title']) { $items[t('Full template')] = implode("
\n", $node->fb_feed_data['full']); } $node->content['fb_feed'] = array('#value' => theme('dl', $items)); return $node; } /** * Publish a feed message via the Feed Dialog. This will prompt the * user before writing to the feed, and this is Facebook's recommended * technique. The other option is publishUserAction. * * Our approach here is like drupal_set_message. We save the data to * the session, and show the user the dialog on the next page request. */ function fb_feed_show_dialog($fb_feed_nid, $tokens, $options) { if (!isset($_SESSION['fb_feed_dialogs'])) $_SESSION['fb_feed_dialogs'] = array(); // Accept either nid or template as first param. if (is_object($fb_feed_nid)) { $template = $fb_feed_nid; } else { $template = node_load($fb_feed_nid); } if ($template) { // Change token data structure. if (is_object($tokens) && is_array($tokens->tokens)) { foreach ($tokens->tokens as $i => $key) { $fb_tokens[$key] = $tokens->values[$i]; } } else { $fb_tokens = $tokens; } if (!isset($_SESSION['fb_feed_dialogs'][$template->apikey])) { $_SESSION['fb_feed_dialogs'][$template->apikey] = array(); } $_SESSION['fb_feed_dialogs'][$template->apikey][] = array( 'fb_feed_nid' => $template->nid, 'apikey' => $template->apikey, 'bundle_id' => $template->bundle_id, 'tokens' => $fb_tokens, 'options' => $options, ); } else { // TODO: log error. } } /** * Implementation of hook_fb(). */ function fb_feed_fb($op, $data, &$return) { //dpm("fb_feed_fb($op)"); if ($op == FB_OP_CONNECT_JS_INIT || $op == FB_OP_CANVAS_FBJS_INIT) { //dpm(func_get_args(), 'XXX'); //dpm($_SESSION['fb_feed_dialogs'], 'SESSION[fb_feed_dialogs]'); //XXX } if (($op == FB_OP_CONNECT_JS_INIT || $op == FB_OP_CANVAS_FBJS_INIT) && isset($_SESSION['fb_feed_dialogs']) && isset($_SESSION['fb_feed_dialogs'][$data['fb_app']->apikey]) ) { // Note that FB.Connect.ShowFeedDialog and Facebook.showFeedDialog take different parameters. Thanks, facebook! // Use javascript to publish feed messages foreach ($_SESSION['fb_feed_dialogs'][$data['fb_app']->apikey] as $d) { // accept tokens for template_data (backward compatibility if ($d['tokens']) $d['options']['template_data'] = $d['tokens']; if ($op == FB_OP_CONNECT_JS_INIT) { // http://wiki.developers.facebook.com/index.php/JS_API_M_FB.Connect.ShowFeedDialog $args = array($d['bundle_id']); foreach (array('template_data' => '{}', 'target_ids' => 'null', // note taget_ids array 'body_general' => 'null', 'story_size' => 'null', 'require_connect' => 'null', 'callback' => 'null', 'user_message_prompt' => 'null', 'user_message' => 'null', ) as $key => $default) { if (isset($d['options'][$key])) { if (in_array($key, array('require_connect', 'callback'))) $args[] = $d['options'][$key]; // no json_encode else if ($key == 'user_message' && is_string($d['options'][$key])) { $args[] = '{value:' . json_encode($d['options'][$key]) . '}'; } else $args[] = json_encode($d['options'][$key]); } else $args[] = $default; } $return[] = "FB.Connect.showFeedDialog(" . implode(',', $args) . ");"; } else if ($op == FB_OP_CANVAS_FBJS_INIT) { // http://wiki.developers.facebook.com/index.php/Facebook.showFeedDialog $args = array($d['bundle_id']); foreach (array('template_data' => 'null', 'body_general' => 'null', 'target_id' => 'null', // note target_id int 'continuation' => 'null', 'user_message_prompt' => 'null', 'user_message' => 'null', ) as $key => $default) { if (isset($d['options'][$key])) { if (in_array($key, array('continuation'))) $args[] = $d['options'][$key]; // no json_encode else if ($key == 'user_message' && is_string($d['options'][$key])) { $args[] = '{value:' . json_encode($d['options'][$key]) . '}'; } else $args[] = json_encode($d['options'][$key]); } else $args[] = $default; } //dpm($args, "Facebook.showFeedDialog args"); $return[] = "Facebook.showFeedDialog(" . implode(",\n", $args) . ");"; } } // remove displayed items from session unset($_SESSION['fb_feed_dialogs'][$data['fb_app']->apikey]); } } //// Feed Actions function fb_feed_action_info() { $items = array(); $items['fb_feed_action_publish'] = array( 'type' => 'fb_feed_action', 'description' => t('Facebook Feed: publishUserAction'), 'configurable' => TRUE, // Include 'custom' so third-party modules can define their own triggers. 'hooks' => array( 'nodeapi' => array('delete', 'insert', 'update', 'view', 'custom'), 'comment' => array('delete', 'insert', 'update', 'view', 'custom'), 'user' => array('insert', 'update', 'delete', 'login', 'logout', 'custom'), ), ); $items['fb_feed_action_show_dialog'] = array( 'type' => 'fb_feed_action_show_dialog', 'description' => t('Facebook Feed: show dialog'), 'configurable' => TRUE, // Include 'custom' so third-party modules can define their own triggers. 'hooks' => array( 'nodeapi' => array('delete', 'insert', 'update', 'view', 'custom'), 'comment' => array('delete', 'insert', 'update', 'view', 'custom'), 'user' => array('insert', 'update', 'delete', 'login', 'logout', 'custom'), ), ); return $items; } function fb_feed_get_template_options() { $options = array(0 => t('')); $result = db_query(db_rewrite_sql("SELECT DISTINCT n.nid, n.title FROM {node} n WHERE n.type='%s' AND n.status=1"), FB_FEED_NODE_TYPE_TEMPLATE); while ($data = db_fetch_object($result)) { $options[$data->nid] = $data->title; } return $options; } function fb_feed_action_publish_form($values) { // Allow user to choose amoung all available templates. $options = fb_feed_get_template_options(); $form['description'] = array('#value' => t('Note that this action will only succeed when executed from a Facebook canvas page when the user is logged in, or on non-canvas pages when the local user has authorized offline access. These are privacy restrictions enforced by the Facebook API.')); $form['fb_feed_template_nid'] = array('#type' => 'select', '#title' => t('Template'), '#default_value' => $values['fb_feed_template_nid'], '#options' => $options, '#description' => t('Which template will we be using?'), '#required' => TRUE, ); $form['token_enable'] = array('#type' => 'checkbox', '#title' => t('Use token replacement'), '#default_value' => $values['token_enable'], '#description' => t('Use token module for template substitution.'), ); $form['hook_help'] = array('#type' => 'markup', '#value' => t('In addition to token replacement, hook_fb_feed will be called before Feed.publishUserAction is called. This hook gives you an oportunity to customize the parameters that will be passed to Facebook, for example to provide values for custom tokens in your template. Until further documentation is provided, you should look in the code for exactly how these functions are called.'), '#prefix' => '

', '#suffix' => '

', ); return $form; } function fb_feed_action_publish_validate($form_id, $values) { // TODO } function fb_feed_action_publish_submit($form_id, $values) { $items = array(); foreach (array('fb_feed_template_nid', 'token_enable') as $key) { $items[$key] = $values[$key]; } return $items; } function fb_feed_action_publish(&$context, $values = array()) { //dpm(func_get_args(), 'fb_actions_minifeed'); // Get the objects we're acting upon. $objects = array(); if ($values['hook'] == 'nodeapi') { $objects['node'] = $values['node']; } else if ($values['hook'] == 'comment') { $objects['comment'] = $values['comment']; $objects['node'] = node_load($values['comment']->nid); } else if ($values['hook'] == 'user') { $account = $values['user']; $objects['user'] = $account; } // Get the template $template = node_load($values['fb_feed_template_nid']); // TODO: Sanity check that bundle has been registered with Facebook. // And the app $fb_app = fb_get_app(array('nid' => $template->fb_app_nid)); $objects['fb_app'] = $fb_app; // TODO: Sanity check that apikeys match. // Log into facebook as the current user. if ($GLOBALS['fb_app'] && ($fb_app->nid = $GLOBALS['fb_app']->nid)) // We're in a canvas page for the desired app. We're already logged in. $fb = $GLOBALS['fb']; else { global $user; // Must log in to publish user action. This will only work if the user // have authorized us for offline access. $fbu = fb_get_fbu($user->uid, $fb_app); if ($fbu) { $fb = fb_api_init($fb_app, $fbu); } } // It's possible we have an $fb, but session may not be valid. TODO: find a // way to test this? if ($fb) { // We need to pass a bunch of parameters to Feed.publishUserAction. $params = array('bundle_id' => $template->bundle_id, 'tokens' => array(), 'target_ids' => array(), 'body_general' => '', 'do_publish' => TRUE, ); $options = array('fb_feed_template' => $template); if ($values['token_enable']) { // Use tokens for every kind of object we know about foreach (array('node', 'comment', 'user', 'fb_app') as $type) { if ($objects[$type]) { $toks = token_get_values($type, $objects[$type], FALSE, $options); //watchdog('fb_feed_debug', "token_get_values($type) returned " . dprint_r($toks, 1)); if ($toks && is_array($toks->tokens)) { foreach ($toks->tokens as $i => $key) $params['tokens'][$key] = $toks->values[$i]; } } } } // Use naming conventions to allow tokens to provide every parameter. if ($params['tokens']['target_ids']) $params['target_ids'][] = $params['tokens']['target_ids']; if ($params['tokens']['body_general']) $params['body_general'] = $params['tokens']['body_general']; // Invoke a hook so that other modules have a chance to modify the params before we pass them to facebook. $params = fb_feed_invoke($fb_app, FB_FEED_OP_TOKEN_ALTER, $params, array('fb_feed_template' => $template, 'objects' => $objects, 'context' => $context)); if (fb_verbose()) { watchdog('fb_feed', t("Publish user action, app is %app, bundle is %bundle_id, params are !params.", array('%app' => $fb_app->title, '%bundle_id' => $template->bundle_id, '!params' => dprint_r($params, 1)))); } if (is_array($params['tokens']) && $params['do_publish']) { if (is_array($params['target_ids'])) $target_ids = implode(',', $params['target_ids']); else $target_ids = $params['target_ids']; try { // http://wiki.developers.facebook.com/index.php/Feed.publishUserAction //if (FALSE) // disabled for debugging $fb->api_client->feed_publishUserAction($template->bundle_id, json_encode($params['tokens']), $target_ids, $params['body_general']); } catch (Exception $e) { // We can get a lot of "Feed action limit reached" exceptions. So // only log if fb_verbose is true. if ($e->getCode() != 341 || fb_verbose()) fb_log_exception($e, t('Failed to publish user action')); } } } } // show feed dialog action function fb_feed_action_show_dialog_form($values) { // Allow user to choose from all available templates. $options = fb_feed_get_template_options(); $form['description'] = array( '#value' => t('Note that this action will only succeed when executed from a Facebook canvas page or Facebook Connect page when the user is logged in. These are privacy restrictions enforced by the Facebook API.')); $form['fb_feed_template_nid'] = array( '#type' => 'select', '#title' => t('Template'), '#default_value' => $values['fb_feed_template_nid'], '#options' => $options, '#description' => t('Which template will we be using?'), '#required' => TRUE, ); $form['token_enable'] = array( '#type' => 'checkbox', '#title' => t('Use token replacement'), '#default_value' => $values['token_enable'], '#description' => t('Use token module for template substitution.'), ); $form['user_message_prompt'] = array( '#type' => 'textfield', '#title' => t('Prompt'), '#description' => t('The label (which could be in the form of a question) that appears above the text box on the Feed form next to the Facebook-provided question, "What\'s on your mind?".'), '#default_value' => $values['user_message_prompt'], ); return $form; } function fb_feed_action_show_dialog_validate($form_id, $values) { // TODO } function fb_feed_action_show_dialog_submit($form_id, $state) { $items = array(); foreach (array('fb_feed_template_nid', 'token_enable', 'user_message_prompt') as $key) { $items[$key] = $state[$key]; } return $items; } function fb_feed_action_show_dialog(&$context, $values = array()) { //dpm(func_get_args(), "fb_feed_action_show_dialog"); // Get the template $template = node_load($values['fb_feed_template_nid']); // Params we will pass to alter hook, then to fb_feed_show_dialog $params = array( 'template' => $template, 'tokens' => array(), 'options' => array( 'user_message_prompt' => $values['user_message_prompt'], ), 'cancel' => FALSE, // Allow alter hook to cancel dialog. ); // Get the objects we're acting upon. $objects = array(); if ($values['hook'] == 'nodeapi') { $objects['node'] = $values['node']; $params['options']['user_message'] = check_plain($objects['node']->body); } else if ($values['hook'] == 'comment') { $objects['comment'] = $values['comment']; $objects['node'] = node_load($values['comment']->nid); $params['options']['user_message'] = check_plain($objects['comment']->comment); } else if ($values['hook'] == 'user') { $account = $values['user']; $objects['user'] = $account; } // TODO: Sanity check that bundle has been registered with Facebook. // And the app $fb_app = fb_get_app(array('nid' => $template->fb_app_nid)); $objects['fb_app'] = $fb_app; // TODO: Sanity check that apikeys match. if ($values['token_enable']) { // Use tokens for every kind of object we know about foreach (array('node', 'comment', 'user', 'fb_app') as $type) { if ($objects[$type]) { $toks = token_get_values($type, $objects[$type], FALSE, $options); //watchdog('fb_feed_debug', "token_get_values($type) returned " . dprint_r($toks, 1)); if ($toks && is_array($toks->tokens)) { foreach ($toks->tokens as $i => $key) dpm($toks->values[$i], "Token for $key is " . strlen($toks->values[$i]) . " chars"); $params['tokens'][$key] = $toks->values[$i]; } } } } // Invoke a hook so that other modules have a chance to modify the params before we pass them to facebook. fb_drupal_alter('fb_feed_show_dialog', $params, array('context' => $context, 'objects' => $objects)); //dpm($params, "params after fb_drupal_alter"); /* // Invoke a hook so that other modules have a chance to modify the params before we pass them to facebook. $params = fb_feed_invoke($fb_app, FB_FEED_OP_TOKEN_ALTER, $params, array('template' => $template, 'objects' => $objects, 'context' => $context)); */ if (!$params['cancel']) { fb_feed_show_dialog($params['template'], $params['tokens'], $params['options']); } } /** * Implementation of hook_fb_feed */ function fb_feed_fb_feed($fb_app, $op, &$return, $data) { if ($op == FB_FEED_OP_TOKEN_ALTER) { dpm(func_get_args(), "fb_feed_fb_feed($op)"); // In this hook, we have a chance to add tokens before a user action is // published. if ($return['tokens']['nid'] && $return['tokens']['title'] && $return['tokens']['fb-app-url']) { // Add a link to a canvas page $return['tokens']['fb-feed-node-link'] = l($return['tokens']['title-raw'], $return['tokens']['fb-app-url'].'/node/'.$return['tokens']['nid'], array(), NULL, NULL, NULL, TRUE); } if ($return['tokens']['fb-app-title'] && $return['tokens']['fb-app-url']) { $return['tokens']['fb-feed-app-link'] = l($return['tokens']['fb-app-title'], $return['tokens']['fb-app-url'], array(), NULL, NULL, NULL, TRUE); } } } /** * Invoke hook_fb_feed. */ function fb_feed_invoke($fb_app, $op, $return = NULL, $data = NULL) { foreach (module_implements(FB_FEED_HOOK) as $name) { $function = $name . '_' . FB_FEED_HOOK; $function($fb_app, $op, $return, $data); } return $return; } ?>