<?php

/**
 * @file
 */

include_once dirname(__FILE__) . '/facebook_rules.inc';

/**
 * Implements hook_perm().
 */
function facebook_rules_permission() {
  return array(
    'facebook_rules_configure_cron' => array(
      'title' => 'Configure facebook rules cron settings',
      'warning' => 'Warning: Give to trusted roles only;',
    ),
  );
}

/**
 * Implements hook_menu().
 */
function facebook_rules_menu() {

  $items['admin/config/people/facebook_rules'] = array(
    'title' => t('Facebook Rules cron settings'),
    'page callback' => 'drupal_get_form',
    'page arguments' => array('facebook_rules_settings_form'),
    'access arguments' => array('facebook_rules_configure_cron'),
    'file' => 'facebook_rules.admin.inc',
    'description' => 'Configure facebook rules cron settings.',
    'type' => MENU_NORMAL_ITEM,
  );

  return $items;
}

/**
 * Implements hook_help().
 */
function facebook_rules_help($path, $arg) {
  switch ($path) {
    case 'admin/help#facebook_rules':
      return t('Earlier facebook provided an extended permission called offline_access that provided lifetime token validity. However after its depreciation, all the tokens are restricted to 60 days valid period. A valid access token can be exchanged for a new token that would again come with 60 day validity, hence virtually extending the token validity to infinite time.<p>The cron run gets all the user tokens less than specified days and exchange them with new tokens.<p>') . l('Issue on drupal.org', 'http://drupal.org/node/1481112');
  }
}

/**
 * Implements hook_form_form_id_alter().
 */
function facebook_rules_form_user_profile_form_alter(&$form, &$form_state) {

$user_token_status = facebook_rules_get_token_status($form['#user']->uid);
// Display a button to generate token if the access token has expired or access token is not set.
  if (fboauth_fbid_load($form['#user']->uid)) {
  
    if ($user_token_status == '1' || !facebook_rules_get_token($form['#user']->uid)) {
    
      $form['account']['facebook_rules'] = array(
        '#type' => 'item',
        '#weight' => '-6',
        '#description' => t("If you see this button, you need to generate your Facebook access token. Either you have not generated it yet or it has expired."),
        '#markup' => fboauth_action_display('facebook_rules_generate_token'),
      );
      
    }

  $form['account']['facebook_rules_allow'] = array(
      '#type' => 'checkbox',
      '#weight' => '-6',
      '#title' => t("Allow " . variable_get('site_name', '') . " to post to your facebook wall on your behalf."),
      '#default_value' => facebook_rules_profile_user_grant($form['#user']->uid),
      '#disabled' => ($user_token_status == '1' || !facebook_rules_get_token($form['#user']->uid)) ? TRUE : FALSE,
      '#description' => ($user_token_status == '1' || !facebook_rules_get_token($form['#user']->uid)) ? 'You need to have a valid access token to set or unset this option.' : '',
    );
    
  }

  $form['#submit'][] = 'facebook_rules_profile_submit';

}

/**
 * Submit handler for hook_form_form_id_alter.
 */
function facebook_rules_profile_submit(&$form, $form_state) {

$linked = fboauth_fbid_load($form['#user']->uid);
if (isset($form_state['values']['facebook_rules_allow'])) {
  if ($linked) {
    db_merge('facebook_rules_user_tokens')
    ->key(array('uid' => $form_state['user']->uid))
    ->fields(array(
        'user_grant' => $form_state['values']['facebook_rules_allow'],
    ))
    ->execute();
  }
}
}

/**
 * Implements hook_fboauth_actions().
 */
function facebook_rules_fboauth_actions() {
  $actions = array();

  $actions['facebook_rules_generate_token'] = array(
    'title' => t('Generate facebook token'),
    'callback' => 'facebook_rules_generate_token',
    'permissions' => array(
      'publish_stream',
      ),
    );
  
  //overwrite fboauth connect callback
  //to add more permissions and store user's access token.
  $actions['connect']['permissions'] = array_keys(fboauth_user_connect_permissions());
  $actions['connect']['permissions'][] = 'publish_stream';
  $actions['connect']['callback'] = 'facebook_rules_fboauth_action_connect';

  //overwrite fboauth deauth callback
  //to remove user's stored access token.
  $actions['deauth']['title'] = t('Deauthorize');
  $actions['deauth']['callback'] = 'facebook_rules_fboauth_action_deauth';

  return $actions;
}

/**
 * Post message on user's wall.
 * This function can be reused to post different $data on facebook wall.
 */
function facebook_rules($url, $data) {

$content = facebook_rules_post($url, $data);

  if (array_key_exists('id', $content) && $content['id']) {
    //This message could have been displayed using rules at admin's discretion.
    //However it seems more ethical to let the user know what action has been posted to his wall asa it is posted.
    //A cleaner way to disable this message could be http://drupal.org/project/disable_messages, iff neccesary.
    drupal_set_message(t('Activity posted on your facebook wall'), 'status');
  }
  else {
    global $user;
    // If access token has expired or not set.
    if ($content['error']['code'] == '190') {
      // Set a variable upon unsuccessful post attempt.
      facebook_rules_set_token_status($user->uid, '1');
      $link = fboauth_action_link_properties('facebook_rules_generate_token');
      $token_url = url($link['href'], array('query' => $link['query']));
      drupal_set_message(t("It seems your access token for Facebook has expired. !link to renew.", array('!link' => l('Click here', $token_url))), 'warning');
    }
    // If there is possibility of duplicate status message.
    elseif ($content['error']['code'] == '506') {
      drupal_set_message(t("This status was posted recently on your facebook wall. Duplicate message suspected."), 'warning');
    }
    // Facebook allows a maximum number of status updates in a day. In case the limit is reached, show a warning.
    elseif ($content['error']['code'] == '341') {
      drupal_set_message(t("Facebook feed action request limit reached. Your updates would no longer be posted on your Facebook wall today."), 'warning');
    }
    elseif (!facebook_rules_get_token($user->uid) && fboauth_fbid_load($user->uid)) {
      $link = fboauth_action_link_properties('facebook_rules_generate_token');
      $token_url = url($link['href'], array('query' => $link['query']));
      drupal_set_message(t("It seems your access token for Facebook is not set. !link to generate.", array('!link' => l('Click here', $token_url))), 'warning');
    }
    // For all other errors.
    else {
      drupal_set_message(t("Something went wrong while posting update to your facebook wall. The error has been captured by watchdog."));
    }

  // Log error message to watchdog.
  watchdog('facebook_rules', 'Message: :msg<br />Type: :type<br />Code: :code<br />User: :uid', array(':msg' => $content['error']['message'], 'type' => $content['error']['type'], ':code' => $content['error']['code'], ':uid' => $user->uid), WATCHDOG_ERROR);

  // Store the failed message and url in session.
  $_SESSION['facebook_rules-' . $user->uid . '-failed_update-message'] = $data;
  $_SESSION['facebook_rules-' . $user->uid . '-failed_update-url'] = $url;

  }
}

/**
 * Facebook OAuth callback for initiating a Facebook connection.
 * Borrowed from fboauth.module: Overridden.
 */
function facebook_rules_fboauth_action_connect($app_id, $access_token) {
  global $user;

  $fbuser = fboauth_graph_query('me', $access_token);
  $uid = fboauth_uid_load($fbuser->id);

  // If the user has logged in before, load their account and login.
  if (!$user->uid && $uid && ($account = user_load($uid))) {
    fboauth_login_user($account);
    // stores access token for the user.
    facebook_rules_store_token($access_token, $account->uid);
    // invoke rules event.
    rules_invoke_event('facebook_rules_login_through_facebook', $account);
      
  }
  // If the Facebook e-mail address matches an existing account, bind them
  // together and log in as that account.
  elseif (!empty($fbuser->email) && ($account = array_shift(user_load_multiple(array(), array('mail' => $fbuser->email))))) {
    fboauth_save($account->uid, $fbuser->id);
    // stores access token for the user.
    facebook_rules_store_token($access_token, $account->uid);
    // Logins will be denied if the user's account is blocked.
    if (fboauth_login_user($account)) {
      drupal_set_message(t('You\'ve connected your account with Facebook.'));
    }
  }
  else {
    // If the user is already logged in, associate the two accounts.
    if ($user->uid) {
      // stores access token for the user.
      facebook_rules_store_token($access_token, $user->uid);
      // invoke rules event.
      rules_invoke_event('facebook_rules_linked_to_facebook', $user);

      fboauth_save($user->uid, $fbuser->id);
      drupal_set_message(t('You\'ve connected your account with Facebook.'));
    }
    // Register a new user only if allowed.
    elseif (variable_get('user_register', 1)) {
      $account = fboauth_create_user($fbuser);
      // Load the account fresh just to have a fully-loaded object.
      $account = user_load($account->uid);
      // stores access token for the user.
      facebook_rules_store_token($access_token, $account->uid);
      // If the account requires administrator approval the new account will
      // have a status of '0' and not be activated yet.
      if ($account->status == 0) {
        // stores access token for the user.
        facebook_rules_store_token($access_token, $account->uid);
        // invoke rules event.
        rules_invoke_event('facebook_rules_registered_through_fbconnect_requires_approval', $account);

        _user_mail_notify('register_pending_approval', $account);
        drupal_set_message(t('An account has been created for you on @sitename but an administrator needs to approve your account. In the meantime, a welcome message with further instructions has been sent to your e-mail address.', array('@sitename' => variable_get('site_name', ''))));
      }
      // Log in the user if no approval is required.
      elseif (fboauth_login_user($account)) {
        // stores access token for the user.
        facebook_rules_store_token($access_token, $account->uid);
        // invoke rules event.
        rules_invoke_event('facebook_rules_registered_through_fbconnect', $account);
        
        drupal_set_message(t('Welcome to @sitename. Basic information has been imported from Facebook into your account. You may want to <a href="!edit">edit your account</a> to confirm the details and set a password.', array('@sitename' => variable_get('site_name', ''), '!edit' => url('user/' . $account->uid . '/edit'))));
      }
      // If the login fails, fboauth_login_user() throws its own error message.
    }
    // Since user's can't create new accounts on their own, show an error.
    else {
      drupal_set_message(t('Your Facebook e-mail address does not match any existing accounts. If you have an account, you must first log in before you can connect your account to Facebook. Creation of new accounts on this site is disabled.'));
    }
  }
  
}

/**
 * Facebook OAuth callback for deauthorizing a Facebook connection.
 * Borrowed from fboauth.module: Overridden.
 */
function facebook_rules_fboauth_action_deauth($app_id, $access_token) {
  global $user;

  // Deauthorize our application from Facebook.
  $result = fboauth_method_invoke('auth.revokeAuthorization', $access_token);

  // If successful, also remove the uid-fbid pairing.
  if (!is_array($result) && $result) {
    //remove stored token for the user.
    facebook_rules_remove_token($user->uid);
    fboauth_save($user->uid, NULL);
    drupal_set_message(t('Your account has been disconnected from Facebook.'));
  }
  else {
    drupal_set_message(t('There was an error disconnecting from Facebook. The server returned %message.', array('%message' => $result->error_msg)), 'error');
    watchdog('There was an error disconnecting the user %username from Facebook. The server returned %message.', array('%message' => $result->error_msg, '%username' => $user->name));
  }
}

/**
 * Implements hook_user_delete().
 */
function facebook_rules_user_delete($account) {
  facebook_rules_remove_token($account->uid);
}

/**
 * Implements hook_user_cancel().
 */
function facebook_rules_user_cancel($edit, $account, $method) {
  facebook_rules_remove_token($account->uid);
}

/**
 * Implements hook_cron().
 */
function facebook_rules_cron() {
  $renewal_limit = variable_get('facebook_rules_cron_limit', '50');

  $tokens_to_renew = db_query_range('SELECT uid FROM {facebook_rules_user_tokens} WHERE FROM_UNIXTIME(timestamp) < NOW() - INTERVAL :period DAY', 0, $renewal_limit, array(':period' => variable_get('facebook_rules_renewal_days_interval', '50')));

  while ($uid_to_renew = $tokens_to_renew->fetchAssoc()) {
    facebook_rules_renew_token($uid_to_renew['uid']);
  }

}

