You are here

user_relationships_api.module in User Relationships 6

Same filename and directory in other branches
  1. 5.3 user_relationships_api/user_relationships_api.module

User Relationships API. Module shell. @author Jeff Smick (creator) @author Alex Karshakevich (maintainer) http://drupal.org/user/183217

File

user_relationships_api/user_relationships_api.module
View source
<?php

/**
 * @file
 * User Relationships API. Module shell.
 * @see user_relationships_api.api.inc
 * @author Jeff Smick (creator)
 * @author Alex Karshakevich (maintainer) http://drupal.org/user/183217
 */
define('UR_OK', 0x0);
define('UR_BANNED', 0x1);

// Pre Loading files that will be required in this module
foreach (array(
  'api',
  'actions',
  'socnet',
) as $file) {
  module_load_include("inc", "user_relationships_api", "user_relationships_api.{$file}");
}
if (module_exists('privatemsg')) {
  module_load_include('inc', 'user_relationships_api', 'user_relationships_api.privatemsg');
}

/**
 *  Invoke hook_user_relationships() or hook_user_relationships_type()
 *  Just a helper that allows us to pass the relationship or relationship_type
 *  object by reference
 */
function _user_relationships_invoke($type, &$relationship, $is_type = FALSE) {
  $hook = 'user_relationships' . ($is_type ? '_type' : '');
  foreach (module_implements($hook) as $module) {
    $function = $module . '_' . $hook;
    $function($type, $relationship);
  }
}

/**
 * Helper function to generate the main and count queries from a list of parameters and options
 */
function _user_relationships_generate_query($param = array(), $options = array()) {
  extract($options, EXTR_SKIP);

  // Doing a JOIN on the {user_relationship_types} table is rather slow as
  // MySQL has to use ALL for the table join. On most sites, there will only be
  // a few relationship types, and there will nearly always be a far higher
  // number of relationships. So, we split the query out into PHP saving us
  // from having to do the JOIN.
  $relationship_types = user_relationships_types_load();
  $oneway_relationship_type_ids = array();
  foreach ($relationship_types as $rt) {
    if ($rt->is_oneway) {
      $oneway_relationship_type_ids[] = $rt->rtid;
    }
  }
  $oneway_relationship_types_in = implode(",", $oneway_relationship_type_ids);
  $twoway_reverse_clause = FALSE;

  //#479486

  // 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':
        $between_cond = "((ur.requester_id {$operator[0]} AND ur.requestee_id {$operator[1]}) OR (";

        //#479486 do not include reverse records of two-way relationships by default
        if (!isset($include_twoway_reverse)) {
          if (!empty($oneway_relationship_types_in)) {
            $between_cond .= '(ur.approved <> 1 OR ur.rtid IN (' . $oneway_relationship_types_in . ')) AND ';
          }
          else {
            $between_cond .= '(ur.approved <> 1) AND ';
          }
          $twoway_reverse_clause = TRUE;
        }
        $between_cond .= "ur.requestee_id {$operator[0]} AND ur.requester_id {$operator[1]}))";
        $cond[] = $between_cond;
        $arguments[] = $value[0];
        $arguments[] = $value[1];
        $arguments[] = $value[0];
        $arguments[] = $value[1];
        break;
      case 'user':

        //#479486 when showing all user's relationships, do not include reverse records of two-way relationships
        if (!isset($include_twoway_reverse)) {
          if (!empty($oneway_relationship_types_in)) {
            $cond[] = "(ur.requester_id {$operator} OR ((ur.approved <> 1 OR ur.rtid IN (" . $oneway_relationship_types_in . ")) AND ur.requestee_id {$operator}))";
          }
          else {
            $cond[] = "(ur.requester_id {$operator} OR (ur.approved <> 1 AND ur.requestee_id {$operator}))";
          }
          $twoway_reverse_clause = TRUE;
        }
        else {
          $cond[] = "(ur.requester_id {$operator} OR ur.requestee_id {$operator})";
        }
        $arguments[] = $value;
        $arguments[] = $value;
        break;
      case 'rid':
      case 'requester_id':
      case 'requestee_id':
        $twoway_reverse_clause = TRUE;

      //#479486 these columns automatically should exclude duplicates
      default:
        $types_cols = array(
          'name',
          'plural_name',
          'is_oneway',
          'is_reciprocal',
          'requires_approval',
          'expires_val',
        );
        $cond[] = "%s.%s {$operator}";
        $arguments[] = !in_array($key, $types_cols) ? 'ur' : 'urt';
        $arguments[] = $key;

        //#358669 support having multiple values for a single column - the query already has IN(...) placeholders
        if (!is_array($value)) {
          $arguments[] = $value;
        }
        else {
          $arguments = array_merge($arguments, $value);
        }
    }
  }

  //#479486 add a general clause that removed reverse direction from two-way relationship results, unless it's been addressed above
  if (!$twoway_reverse_clause && !isset($include_twoway_reverse)) {
    if (!empty($oneway_relationship_types_in)) {
      $cond[] = '(ur.rtid IN (' . $oneway_relationship_types_in . ') OR ur.approved <> 1 OR ur.requester_id < ur.requestee_id)';
    }
    else {
      $cond[] = '(ur.approved <> 1 OR ur.requester_id < ur.requestee_id)';
    }
    $twoway_reverse_clause = TRUE;
  }
  $selects = array(
    'DISTINCT ur.rid',
    'ur.*',
    'urt.*',
  );
  $joins = array(
    'INNER JOIN {user_relationship_types} urt ON ur.rtid = urt.rtid',
  );

  // We wont need anything after this point for the count SQL
  $count_joins = implode(' ', $joins);
  if (isset($include_user_info) && $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(
      'INNER JOIN {users} requesters ON ur.requester_id = requesters.uid',
      'INNER 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 = is_array($extra) ? implode(' ', $extra) : $extra;
  return array(
    //331692 remove improper GROUP BY rid for Postgre
    'query' => "SELECT {$selects} FROM {user_relationships} ur {$joins} {$cond} {$extra}",
    'count' => "SELECT COUNT(DISTINCT 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 {
      $type = is_numeric(current($value)) ? 'int' : 'varchar';
      return 'IN (' . db_placeholders($value, $type) . ')';
    }
  }
  elseif (is_numeric($value) || is_bool($value)) {
    $value = (int) $value;
    return '= %d';
  }
  elseif (is_float($value)) {
    return '= %f';
  }
  elseif (preg_match('/([<>=]{1,2})\\s*(.+)/', $value, $matches)) {
    $marker = "'%s'";
    $value = $matches[2];
    if (is_numeric($value)) {
      $marker = '%d';
      $value = (int) $value;
    }
    return "{$matches[1]} {$marker}";
  }
  else {
    return "= '%s'";
  }
}

/**
 * hook_perm()
 */
function user_relationships_api_perm() {
  return array(
    'can have relationships',
  );
}

/**
 * hook_cron()
 */
function user_relationships_api_cron() {
  $now = time();

  // only expire relationships once a day
  $last_cron = variable_get('user_relationships_last_expire', 0);
  if ($now < $last_cron + 86400) {
    return FALSE;
  }
  $result = db_query(" SELECT ur.rid\n      FROM {user_relationships} ur\n        INNER JOIN {user_relationship_types} urt ON ur.rtid = urt.rtid\n      WHERE ur.approved = 0\n        AND urt.expires_val > 0\n        AND ur.updated_at < (%d - (urt.expires_val * 86400))\n      GROUP BY ur.rid", $now);
  $expired_requests = array();
  while ($request = db_fetch_object($result)) {
    $expired_requests[] = $request->rid;
  }
  if (count($expired_requests)) {
    db_query('DELETE FROM {user_relationships} WHERE rid IN (' . db_placeholders($expired_requests) . ')', $expired_requests);

    //FIXME: this will trigger various hook_user_relationships() but will not pass a valid relationship object. E.g. in mailer.
    _user_relationships_invoke('delete', $expired_requests);
  }

  // remember when we last expired relationships
  variable_set('user_relationships_last_expire', $now);
  return TRUE;
}

/**
 * hook_user()
 */
function user_relationships_api_user($type, &$edit, &$account, $category = NULL) {
  if ($type == 'delete') {
    db_query("DELETE FROM {user_relationships} WHERE requester_id = %d OR requestee_id = %d", $account->uid, $account->uid);
  }
}

/**
 * Wrapper function for tt() if i18nstrings enabled.
 */
function ur_tt($name, $string, $langcode = NULL, $update = FALSE) {
  if (module_exists('i18nstrings')) {
    return tt($name, $string, $langcode, $update);
  }
  else {
    return $string;
  }
}

/**
 * Implementation of hook_user_relationships().
 * or
 * Implementation of hook_trigger_name().
 */
function user_relationships_api_user_relationships($op, &$relationship) {
  if ($op != 'approve' && $op != 'request') {
    return;
  }
  if (module_exists('trigger')) {
    $aids = _trigger_get_hook_aids('user_relationships_api', $op);
    $context = array(
      'hook' => 'user_relationships_api',
      'op' => $op,
      'requester' => $relationship->requester,
      'requestee' => $relationship->requestee,
      'relationship' => $relationship,
    );

    //print_r($relationship);
    actions_do(array_keys($aids), $relationship, $context);
  }
}

/**
 * Implementation of hook_activity_info().
 */
function user_relationships_api_activity_info() {
  $info = new stdClass();
  $info->api = 2;
  $info->name = 'user_relationships_api';
  $info->object_type = 'user_relationships_api';
  $info->objects = array(
    'Requester' => 'requester',
    'Requestee' => 'requestee',
    'Relationship' => 'relationship',
  );
  $info->hooks = array(
    'user_relationships_api' => array(
      'approve',
      'request',
    ),
  );
  foreach (user_relationships_types_load() as $type_obj) {
    $info->realms["user_relationships_" . $type_obj->rtid] = $type_obj->plural_name;
  }
  return $info;
}

/**
* Implementation of hook_token_list().
*/
function user_relationships_api_token_list($type = 'all') {
  $tokens = array();
  if ($type == 'requester') {
    $tokens['user_relationships_api'] = array(
      'requester' => t('The user who issued the connection request.'),
    );
  }
  elseif ($type == 'relationship') {
    $tokens['user_relationships_api'] = array(
      'requestee' => t('The user who approved the connnection request.'),
      'relationship-name' => t('The relationship name (singular form)'),
    );
  }
  return $tokens;
}

/**
* Implementation of hook_token_values().
*/
function user_relationships_api_token_values($type, $data = NULL, $options = array()) {
  if ($type == 'requester') {
    $r = $data;
    $token_values = array(
      'requester' => theme('username', user_load(array(
        'uid' => $r->uid,
      ))),
    );
  }
  elseif ($type == 'requestee') {
    $r = $data;
    $token_values = array(
      'requestee' => theme('username', user_load(array(
        'uid' => $r->uid,
      ))),
    );
  }
  elseif ($type == 'relationship') {

    // http://drupal.org/node/811222 need to unwrap the relationship object from an array sometimes
    if (is_array($data)) {
      foreach ($data as $object) {
        if (is_object($object)) {
          $r = $object;
          break;
        }
      }
    }
    else {
      $r = $data;
    }
    $r_type = user_relationships_type_load((int) $r->rtid);
    $token_values = array(
      'requestee' => theme('username', user_load(array(
        'uid' => $r->requestee_id,
      ))),
      'requester' => theme('username', user_load(array(
        'uid' => $r->requester_id,
      ))),
      'relationship-name' => theme('placeholder', $r_type->name),
    );
  }
  return isset($token_values) ? $token_values : NULL;
}

Functions

Namesort descending Description
ur_tt Wrapper function for tt() if i18nstrings enabled.
user_relationships_api_activity_info Implementation of hook_activity_info().
user_relationships_api_cron hook_cron()
user_relationships_api_perm hook_perm()
user_relationships_api_token_list Implementation of hook_token_list().
user_relationships_api_token_values Implementation of hook_token_values().
user_relationships_api_user hook_user()
user_relationships_api_user_relationships Implementation of hook_user_relationships(). or Implementation of hook_trigger_name().
_user_relationships_generate_query Helper function to generate the main and count queries from a list of parameters and options
_user_relationships_invoke Invoke hook_user_relationships() or hook_user_relationships_type() Just a helper that allows us to pass the relationship or relationship_type object by reference
_user_relationship_process_query_argument Helper function to process the various argument types allowed

Constants

Namesort descending Description
UR_BANNED
UR_OK @file User Relationships API. Module shell. @author Jeff Smick (creator) @author Alex Karshakevich (maintainer) http://drupal.org/user/183217