<?php
// $Id: user_relationships.module,v 1.14.4.27 2008/02/24 23:12:36 sprsquish Exp $

/**
 *
 * This file contains utility functions for the User Relationships module
 * Please consult either the other files for public functions
 *
 */

define('UR_OK', 0x0);
define('UR_BANNED', 0x1);


// Load all needed files
foreach (array(
  'api', 'theme', 'hooks', 'pages', 'forms', 'actions'
  ) as $file_to_include
) { 
  $modulepath = drupal_get_path('module', 'user_relationships');
  include_once("{$modulepath}/user_relationships_{$file_to_include}.inc");
}


/**
 * Returns a nested array of default messages
 */
function user_relationships_default_messages() {
  return array(
    'informational' => array(
      'submitted'     => 'Your request to be a %relationship_name of !requestee has been submitted.',
      'accepted'      => 'You are now a %relationship_name of !requester.',
      'disapproved'   => "!requester's request to be a %relationship_name of !requestee has been removed.",
      'cancel'        => 'Your request to be a %relationship_name of !requestee has been canceled.',
      'default'       => 'No action has been taken.',
      'removed'       => '!requester is no longer a %relationship_name of !requestee.',
      'pending'       => '!requester believes they are a %relationship_name of yours.  Please view your !pending_relationship_requests to approve them.',
      'pre_approved'  => "You're request to be %relationship_name of !requestee has been pre-approved. You are now a %relationship_name of !requestee."
    ),
    'error' => array(
      'too_many_relationships'  => 'You already relate to this user in every possible way.',
      'existing_request'        => 'You have already requested to be a %relationship_name of !requestee',
      'existing_relationship'   => 'You are already a %relationship_name of !requestee',
      'not_accepting_requests'  => 'This user is not accepting relationship requests.',
      'self_request'            => 'You are not allowed to create a relationship to yourself.',
      'non_existant_user'       => 'This user does not exist.',
      'unknown_error'           => 'An unknown error occurred. Please contact the site administrator',
    ),
  );
}


/**
 * Recursively search an array for a key and return the value attached to it
 */
function _user_relationships_get_from_array($needle, &$haystack) {
  foreach ($haystack as $key => $value) {
    if ($key == $needle) {
      return $value;
    }
    else if (is_array($value)) {
      if ($msg = _user_relationships_get_from_array($needle, $value)) {
        return $msg;
      }
    }
  }
}


/**
 * Invoke hook_user_relationships()
 */
function _user_relationships_invoke($type, &$relationship, $category = NULL) {
  foreach (module_list() as $module) {
    $function = $module .'_user_relationships';
    if (function_exists($function)) {
      $function($type, $relationship, $category);
    }
  }
}

/**
 * Adds autocompletion capability
 */
function _user_relationships_autocomplete_types($string = '') {
  $matches = array();
  if ($string) {
    $result = db_query_range("SELECT name FROM {user_relationship_types} WHERE LOWER(name) LIKE LOWER('%%%s%%')", strtolower($string), 0, 10);
    while ($relationship = db_fetch_object($result)) {
      $matches[$relationship->name] = check_plain($relationship->name);
   }
  }
  print drupal_to_js($matches);
  exit();
}

/*
 * Notify the user of pending relationship requests
 */
function _user_relationships_set_notifications(&$account) {
  global $user;

  //only do this for the active user
  if ($account->uid != $user->uid) {
    return;
  }

  $notifications = drupal_get_messages('status', FALSE);
  $notifications = $notifications['status'] ? $notifications['status'] : array();

  $relationships = user_relationships_load(array('requestee_id' => $account->uid, 'approved' => FALSE));
  foreach ($relationships as $relationship) {
    $msg = user_relationships_get_message('pending', $relationship, array(
      '!pending_relationship_requests'  => l(t('pending relationship requests'), "user/{$account->uid}/relationships/requests")
    ));

    if (!in_array($msg, $notifications)) {
      drupal_set_message($msg);
    }
  }
}


/**
 * List of relationships between two users
 */
function _user_relationships_between(&$viewer, &$viewed) {
  $list = array();

  $relationships = user_relationships_load(array('requester_id' => $viewer->uid, 'requestee_id' => $viewed->uid));
  foreach ($relationships as $relationship) {
    if ($relationship->approved && !$list[$relationship->rid]) {
      $list[$relationship->rid] = t('%relationship_name (!remove_link)', array(
        '%relationship_name'  => $relationship->name,
        '!remove_link'        => theme('user_relationships_remove_link', $viewer->uid, $relationship)
      ));
    }
  }

  return $list;
}


/**
 * List of pending relationships with between two users
 */
function _user_relationships_actions_between(&$viewer, &$viewed) {
  if ($viewer->uid == $viewed->uid) {
    return;
  }

  $list = array();

  $relationships = user_relationships_load(array('requester_id' => $viewer->uid, 'requestee_id' => $viewed->uid, 'approved' => FALSE));
  foreach ($relationships as $relationship) {
    $list[] = t('You have requested to be a %relationship_name of this user. (!pending_requests)', array(
      '%relationship_name'  => $relationship->name,
      '!pending_requests'   => l(t('pending requests'), "user/{$viewer->uid}/relationships/requests"),
    ));
  }

  $relationships = user_relationships_load(array('requester_id' => $viewed->uid, 'requestee_id' => $viewer->uid, 'approved' => FALSE));
  foreach ($relationships as $relationship) {
    $list[] = t('This user believes that they are a %relationship_name of yours. (!pending_requests)', array(
      '%relationship_name'  => $relationship->name,
      '!pending_requests'   => l(t('pending requests'), "user/{$viewer->uid}/relationships/requests"),
    ));
  }

  $types_count = (int)db_result(db_query("SELECT COUNT(*) FROM {user_relationship_types}"));
  if (
    variable_get('user_relationships_allow_multiple', TRUE) && 
    (user_relationships_load(array('between' => array($viewer->uid, $viewed->uid)), TRUE) < $types_count)
  ) {
    $list[] = theme('user_relationships_request_relationship_link', $viewed);
  }

  return $list;
}


/**
 * Helper function to build the settings form for the notification messages
 */
function _user_relationships_message_settings_form(&$form, $defaults = array()) {
  foreach ($defaults as $key => $value) {
    if (is_array($value)) {
      $form[$key] = array(
        '#type'         => 'fieldset',
        '#title'        => ucfirst(str_replace('_',' ',$key)),
        '#collapsible'  => TRUE,
        '#collapsed'    => TRUE
      );
      _user_relationships_message_settings_form($form[$key], $value);
    }
    else {
      $form["user_relationships_msg_{$key}"] = array(
        '#type'           => 'textfield',
        '#title'          => ucfirst(str_replace('_',' ',$key)),
        '#default_value'  => variable_get("user_relationships_msg_{$key}", $value)
      );
    }
  }  
}


/**
 * Create the new relationship
 */
function _user_relationships_save_relationship(&$relationship, $op = 'create') {
  if (!isset($relationship->flags)) { $relationship->flags = UR_OK; }

  _user_relationships_invoke('pre-save', $relationship, $op);

  if (!$relationship->created_at) { $relationship->created_at = time(); }

  if ($relationship->rid) {
    // Delete the original request
    db_query('DELETE FROM {user_relationships} WHERE rid = %d', $relationship->rid);

    // Delete possible requests coming the other direction
    $relationship_type = user_relationships_type_load($relationship->rtid);
    if (!$relationship_type->is_oneway) {
      db_query(
        'DELETE FROM {user_relationships} WHERE rtid = %d AND requester_id = %d AND requestee_id = %d',
        $relationship->rtid, $relationship->requestee_id, $relationship->requester_id
      );
    }
  }
  else {
    $relationship->rid = db_next_id('{user_relationships}_id');
  }

  $queries = array();

  $relationship_type = user_relationships_type_load($relationship->rtid);
  $query = 'INSERT INTO {user_relationships} (rid, requester_id, requestee_id, rtid, approved, created_at, updated_at, flags) VALUES';
  $insertions = '(%d, %d, %d, %d, %d, %d, %d, %d)';
  $arguments = array(
    $relationship->rid,
    $relationship->requester_id,
    $relationship->requestee_id,
    $relationship->rtid,
    $relationship->approved,
    $relationship->created_at,
    time(),
    $relationship->flags,
  );
  $queries[] = array("{$query} {$insertions}", $arguments);

  if ($relationship->approved && !$relationship_type->is_oneway) {
    $requester = $arguments[1];
    $arguments[1] = $arguments[2];
    $arguments[2] = $requester;

    $queries[] = array("{$query} {$insertions}", $arguments);
  }

  foreach ($queries as $query) {
    if (db_query($query[0], $query[1]) === FALSE) {
      // If this ever happens we have major problems
      return FALSE;
    }
  }

  _user_relationships_invoke('post-save', $relationship, $op);

  return $relationship;
}


/**
 * Helper function to generate queries from a list of parameters
 */
function _user_relationships_generate_query($param = array(), $order = NULL, $limit = NULL, $include_user_info = FALSE) {
  // Turn the conditions into a query.
  foreach ($param as $key => $value) {
    if (!isset($value)) { continue; }

    $operator = _user_relationship_process_query_argument($key, $value);

    switch ($key) {
    case 'between':
      $cond[] = 
        "((ur.requester_id {$operator[0]} AND ur.requestee_id {$operator[1]})".
        ' OR '.
        "((urt.is_oneway = 1 OR ur.approved = 0) AND ur.requestee_id {$operator[0]} AND ur.requester_id {$operator[1]}))";
      $arguments[] = $value[0];
      $arguments[] = $value[1];
      $arguments[] = $value[0];
      $arguments[] = $value[1];
      break;

    case 'user':
      $cond[] = "(ur.requester_id {$operator} OR ((urt.is_oneway = 1 OR ur.approved = 0) AND ur.requestee_id {$operator}))";
      $arguments[] = $value;
      $arguments[] = $value;
      break;

    default:
      $types_cols = array('name','plural_name','is_oneway','requires_approval','expires_val');
      $cond[] = "%s.%s {$operator}";
      $arguments[] = !in_array($key, $types_cols) ? 'ur' : 'urt';
      $arguments[] = $key;
      $arguments[] = $value;
    }
  }

  $selects = array('ur.rid', 'ur.*', 'urt.*');
  $joins = array('LEFT JOIN {user_relationship_types} urt USING(rtid)');

  // We wont need anything after this point for the count SQL
  $count_joins = implode(' ', $joins);

  if ($include_user_info) {
    $selects = array_merge($selects, array(
      'requesters.name AS requester_name',
      'requestees.name AS requestee_name',
      'requesters.mail AS requester_mail',
      'requestees.mail AS requestee_mail',
      'requesters.data AS requester_data',
      'requestees.data AS requestee_data',
      'requesters.picture AS requester_picture',
      'requestees.picture AS requestee_picture',
    ));

    $joins = array_merge($joins, array(
      'LEFT JOIN {users} requesters ON ur.requester_id = requesters.uid',
      'LEFT JOIN {users} requestees ON ur.requestee_id = requestees.uid'
    ));
  }

  $selects = implode(',', $selects);
  $joins = implode(' ', $joins);

  $cond = $cond ? 'WHERE '.implode(' AND ', $cond) : '';

  $extra = array();
  if (!empty($order)) { 
    $extra[] = "ORDER BY {$order}"; 
  }
  if (!empty($limit)) { 
    $extra[] = "LIMIT {$limit}"; 
  }
  $extra = implode(' ', $extra);

  return array(
    'query'     => "SELECT {$selects} FROM {user_relationships} ur {$joins} {$cond} {$extra}",
    'count'     => "SELECT COUNT(rid) AS count FROM {user_relationships} ur {$count_joins} {$cond}",
    'arguments' => $arguments,
  );
}


/** 
 * Helper function to process the various argument types allowed
 */
function _user_relationship_process_query_argument($key, &$value) {
  if ($key == 'between') {
    return array(
      _user_relationship_process_query_argument(NULL, $value[0]),
      _user_relationship_process_query_argument(NULL, $value[1]),
    );
  }

  if (is_array($value)) {
    if (count($value) == 1) {
      $value = array_shift($value);
      return _user_relationship_process_query_argument($key, $value);
    }
    else {
      $value = implode(',', $value);
      return 'IN (%s)';
    }
  }
  else if (is_numeric($value) || is_bool($value)) {
    $value = (int)$value;
    return '= %d';
  }
  else if (preg_match('/([<>=]{1,2})\s*(.+)/', $value, $matches)){
    $marker = "'%s'";
    $value = $matches[3];

    if (is_numeric($value)) {
      $marker = '%d';
      $value = (int)$value;
    }

    return "{$matches[1]} {$marker}";
  }
  else {
    return "= '%s'";
  }
}
