fb_app); $fb_cron_data = $fb_app_data['fb_cron']; $form['fb_app_data']['fb_cron'] = array('#type' => 'fieldset', '#collapsible' => TRUE, '#collapsed' => TRUE, '#title' => t('Facebook cron settings'), '#description' => t('Allows application-specific actions to be performed during cron jobs. Enable the Actions, Facebook Actions, and Facebook Infinite Session modules to do anything truly useful here.') ); if (module_exists('actions')) { $options = array(); foreach (actions_get_all_actions() as $aid => $action) { if ($action['type'] == t('Facebook App')) $options[$action['type']][$aid] = $action['description']; } $form['fb_app_data']['fb_cron']['actions'] = array('#type' => 'select', '#title' => t('Actions'), '#default_value' => $fb_cron_data['actions'], '#multiple' => TRUE, '#required' => FALSE, '#options' => $options, '#description' => t('Select one or more actions to perform when cron jobs are executed. Note that you should select actions intended to operate on Facebook Applications, and not actions intended to operate on nodes.'), ); } } } /** * hook_cron() * * Perform any actions that have been configured for all known applications. */ function fb_cron_cron() { $all_apps = fb_get_all_apps(); foreach ($all_apps as $fb_app) { $fb_app_data = fb_app_get_data($fb_app); $fb_cron_data = $fb_app_data['fb_cron']; if (count($fb_cron_data['actions']) && function_exists('actions_do')) { // Set paths as if on a canvas page. fb_init_path($fb_app); actions_do($fb_cron_data['actions'], $fb_app); fb_init_path($fb_app, TRUE); // restore original path settings. } } } /** * Implementation of an Action. See Actions module. * * This action will iterate over some number of users, attempting to log into * facebook as that user, then execute some other actions. The idea is to * support cron jobs that do some action on a per-user basis. * */ // This is the old way of defining an action. Deprecated and will eventually // be deleted. function action_fb_cron_per_user($op, $edit = array(), $obj) { // The code in this function requires a lot of knowledge about the // fb_user_app table, fb globals and such. So it really violates the data // separation between modules. Is there a better place for it? Or a better // way to build the APIs? if ($op == 'metadata') { return array('description' => t('Facebook: user-specific actions'), 'type' => t('Facebook App'), // Expects Facebook App as 3rd param 'batchable' => FALSE, 'configurable' => TRUE, ); } else if ($op == 'form') { $form['description'] = array('#value' => t('This action will iterate through user\'s of a Facebook Application, attempt to log into the Facebook API as that user (or failing that, the infinite session user), and execute additional actions. Use this to perform per-user actions during a Facebook cron job.')); foreach (actions_get_all_actions() as $aid => $action) { $options[$action['type']][$aid] = $action['description']; } $form['actions'] = array('#type' => 'select', '#title' => t('Actions'), '#default_value' => $edit['actions'], '#multiple' => TRUE, '#required' => TRUE, '#options' => $options, '#description' => t('Select one or more actions to perform while logged in as a Facebook Application user.'), ); $form['throttle'] = array('#type' => 'textfield', '#title' => t('Throttle'), '#default_value' => $edit['throttle'], '#required' => TRUE, '#description' => t('Number of users to iterate through each time this action is invoked. Recommended: start with a small number and increase when you are sure things are working.'), ); return $form; } else if ($op == 'submit') { return array('actions' => $edit['actions'], 'throttle' => $edit['throttle'], ); } else if ($op == 'do') { // TODO: make sure the app is properly passed in. $fb_app = $obj; // Save current settings $before_fb = $GLOBALS['fb']; $before_fb_app = $GLOBALS['fb_app']; $before_user = $GLOBALS['user']; // Find some users of the app, for whom cron has not run recently. $result = db_query("SELECT * FROM {fb_user_app} WHERE apikey='%s' AND fbu > 0 AND added > 0 ORDER BY time_cron ASC LIMIT %d", $fb_app->apikey, $edit['throttle']); while ($data = db_fetch_object($result)) { // Log into facebook as the user from the database $account = fb_user_get_local_user($data->fbu, $fb_app); if (!$account || !$account->uid) { watchdog('fb cron', t('Facebook user %fbu does not correspond to a local account. Purging bad data from Facebook User table.', array('%fbu' => $data->fbu))); //db_query("DELETE FROM {fb_user_app} WHERE apikey='%s' AND fbu = %d", // $fb_app->apikey, // $data->fbu); } else { // If here, local user has been found. $fb = fb_api_init($fb_app, $data->fbu); if (!$fb || !$fb->api_client->users_getLoggedInUser()) // Failed to log in as the current user, fallback to infinite session. $fb = fb_api_init($fb_app, FB_FBU_INFINITE_SESSION); if (!$fb || !$fb->api_client->users_getLoggedInUser()) { watchdog('fb cron', t('Failed to log into %app during cron. Try testing infinite session key.', array('%app' => $fb_app->title))); } else { // if here, we're logged into facebook. // Set things up as if this were a canvas page. $GLOBALS['user'] = $account; $GLOBALS['fb'] = $fb; $GLOBALS['fb_app'] = $fb_app; // Don't call fb_init_path here, it has already been called during cron jobs. // Invoke any actions that we've been configured to invoke. actions_do($edit['actions'], $fb_app); } // end if able to log into facebook } // end if local user found. // Record how recently cron was run for this user. We do this even if // we failed to log in, because we don't want persistent problems to // clog the cron queue. We'll get to this user again, eventually. db_query("UPDATE {fb_user_app} SET time_cron=%d WHERE apikey='%s' AND fbu=%d", time(), $fb_app->apikey, $data->fbu); } // Restore global variables $GLOBALS['user'] = $before_user; $GLOBALS['fb'] = $before_fb; $GLOBALS['fb_app'] = $before_fb_app; } } ?>