You are here

user_relationship_implications.module in User Relationships 5.2

Drupal Module: User Relationship Implications

@author: Jeff Smick <sprsquish [at] gmail [dot] com>

Allows admins to create implied relationships (eg: Manager implies Coworker)

File

plugins/user_relationship_implications/user_relationship_implications.module
View source
<?php

/**
 * Drupal Module: User Relationship Implications
 *
 * @author: Jeff Smick <sprsquish [at] gmail [dot] com>
 * @file
 * Allows admins to create implied relationships (eg: Manager implies Coworker)
 */

/**
 * Public API to load an implied relationship
 *
 * @param $riid
 *    integer of the implied relationship ID
 *
 * @return
 *    object with the relationship_type object and implied relationship_type object
 */
function user_relationship_implication_load($param = array()) {
  $implied_relationships = user_relationship_implications_load();
  if (is_numeric($param)) {
    return $implied_relationships[$param];
  }
  foreach ($implied_relationships as $implied) {
    $found = TRUE;
    foreach ($param as $column => $value) {
      $column = strtolower($column);
      if ($column == 'name' || $column == 'plural_name') {
        $value = strtolower($value);
        $col_val = strtolower($implied->{$column});
      }
      else {
        $col_val = $implied->{$column};
      }

      // mismatch, move to the next type
      if ($col_val != $value) {
        $found = FALSE;
        break;
      }
    }
    if ($found) {
      return $type;
    }
  }
}

/**
 * Public API to load all implied relationships
 *
 * @return
 *    array of relationship implications
 */
function user_relationship_implications_load($reset = FALSE) {
  static $implications = array();
  if ($reset || !$implications) {
    $results = db_query("SELECT * FROM {user_relationship_implications}");
    while ($implication = db_fetch_object($results)) {
      $implication->relationship_type = user_relationships_type_load($implication->rtid);
      $implication->implies_relationship_type = user_relationships_type_load($implication->implies_rtid);
      $implications[$implication->riid] = $implication;
    }
  }
  return $implications;
}

/**
 * hook_form_alter()
 */
function user_relationship_implications_form_alter($form_id, &$form) {
  switch ($form_id) {
    case 'user_relationships_type_edit':
      $rtid = $form['rtid']['#value'];
      $relationship_type = user_relationships_type_load($rtid);
      $relationship_types = user_relationships_types_load();
      $implied_by = array();
      if ($relationship_type) {
        foreach ($relationship_type->implies as $rtid => $implies) {
          $values[$rtid]['strict'] = $implies->strict;
          $values[$rtid]['reverse'] = $implies->reverse;
        }
        foreach ($relationship_type->implied_by as $implied) {
          if (!$implied->reverse) {
            $implied_by[] = $implied->rtid;
          }
        }
      }
      $form['implications'] = array(
        '#title' => t('This relationship implies'),
        '#type' => 'fieldset',
        '#weight' => 0,
        '#tree' => TRUE,
        '#theme' => 'user_relationship_implications_form_table',
        '#description' => t('
        <ul>
          <li>Users will automatically have these relationships created between them when the implying relationship is created. (ex: Manager implies Coworker).</li>
          <li>When the implied relationship is removed the implying relationship will not be removed. (ex: removing Coworker WILL NOT remove Manager)</li>
          <li>If "strict" is set the implying relationship will be removed when the implied relationship is removed. (ex: removing Coworker WILL remove Manager)</li>
          <li>Reverse is really only useful for one-way relationships. It allows things like Parent implies Offspring to work in the right direction</li>
        </ul>
      '),
      );
      $list = FALSE;
      foreach ($relationship_types as $type) {
        if ($type->rtid != $relationship_type->rtid && !in_array($type->rtid, $implied_by)) {
          $imp_name = "implies_{$type->rtid}";
          $form['implications']['opts'][$type->rtid][$imp_name] = array(
            '#title' => t($type->name),
            '#type' => 'checkbox',
            '#return_value' => $type->rtid,
            '#default_value' => isset($form['#post'][$imp_name]) || isset($values[$type->rtid]),
          );
          $strict_name = "implied_{$type->rtid}_strict";
          $form['implications']['opts'][$type->rtid][$strict_name] = array(
            '#type' => 'checkbox',
            '#return_value' => $type->rtid,
            '#default_value' => isset($form['#post'][$strict_name]) || $values[$type->rtid]['strict'],
          );
          $opp_name = "implied_{$type->rtid}_reverse";
          $form['implications']['opts'][$type->rtid][$opp_name] = array(
            '#type' => 'checkbox',
            '#return_value' => $type->rtid,
            '#default_value' => isset($form['#post'][$opp_name]) || $values[$type->rtid]['reverse'],
          );
          $list = TRUE;
        }
      }
      if ($list) {
        $form['#submit']['user_relationship_implications_edit_submit'] = array();
      }
      else {
        unset($form['implications']);
      }
      break;
  }
}

/**
 * Edit relationship type submission processor
 */
function user_relationship_implications_edit_submit($form_id, &$form_values) {

  // the rtid is in a different place when adding a new type vs. editing an existing type
  if (isset($form_values['relationship_type']) && !is_null($form_values['relationship_type'])) {

    // editing an existing relationship type
    $rtid = $form_values['relationship_type']->rtid;
  }
  else {

    // adding a new relationship type - go figure
    $rtid = $form_values['rtid'];
  }
  db_query("DELETE FROM {user_relationship_implications} WHERE rtid = %d", $rtid);
  foreach ($form_values['implications']['opts'] as $implied_rtid => $elements) {
    if ($elements["implies_{$implied_rtid}"]) {
      $strict = $elements["implied_{$implied_rtid}_strict"] ? 1 : 0;
      $reverse = $elements["implied_{$implied_rtid}_reverse"] ? 1 : 0;
      db_query("INSERT INTO {user_relationship_implications} (riid, rtid, implies_rtid, strict, reverse) VALUES (%d, %d, %d, %d, %d)", db_next_id('{user_relationship_implications}_id'), $rtid, $implied_rtid, $strict, $reverse);
    }
  }
}

/**
 * hook_user_relationships()
 */
function user_relationship_implications_user_relationships($type, &$relationship, $category = NULL) {
  switch ($type) {
    case 'load':
      if ($category == 'type' && $relationship->rtid) {
        static $loaded_relationship_implications = array();
        if (!is_array($loaded_relationship_implications[$relationship->rtid])) {
          $loaded_relationship_implications[$relationship->rtid] = array();
          $results = db_query("SELECT * FROM {user_relationship_implications} WHERE rtid = %d OR implies_rtid = %d", $relationship->rtid, $relationship->rtid);
          while ($implication = db_fetch_object($results)) {
            $loaded_relationship_implications[$relationship->rtid][] = $implication;
          }
        }
        $relationship->implies = array();
        $relationship->implied_by = array();
        foreach ($loaded_relationship_implications[$relationship->rtid] as $implication) {
          if ($implication->rtid == $relationship->rtid) {
            $relationship->implies[$implication->implies_rtid] = $implication;
          }
          else {
            $relationship->implied_by[$implication->rtid] = $implication;
          }
        }
      }
      break;
    case 'delete':
      if ($category == 'type') {
        if (!$relationship->rtid) {
          break;
        }
        $results = db_query("DELETE FROM {user_relationship_implications} WHERE rtid = %d OR implies_rtid = %d", $relationship->rtid, $relationship->rtid);

        // clean out the implications cache
        static $loaded_relationship_implications;
        unset($loaded_relationship_implications);
      }
      else {
        if ($category != 'type') {
          $current_type = user_relationships_type_load($relationship->rtid);

          // nothing else matters if there aren't implications involved
          $reversed = array_filter($current_type->implies, '_user_relationship_implications_filter_for_reverse');
          if (!$current_type->implied_by && !$reversed) {
            break;
          }

          // load relationships that imply this relationship
          $rids = array_merge(array_keys($current_type->implied_by), array_keys($reversed));
          $relationships = user_relationships_load(array(
            'between' => array(
              $relationship->requester_id,
              $relationship->requestee_id,
            ),
            'rtid' => $rids,
          ), FALSE, 'rtid');
          foreach ($relationships as $rtid => $relationship) {
            $relationship = array_shift($relationship);

            // the relationship being deleted (current_type) is implied by this relationship (only matters if "strict" is set)
            if ($current_type->implied_by[$rtid]->strict || $reversed[$rtid]->strict) {
              user_relationships_delete_relationship($relationship, $current_type->deleted_by, $category);
              drupal_set_message(user_relationships_get_message($category, $relationship));
            }
          }
        }
      }
      break;
    case 'post-save':
      $type = user_relationships_type_load($relationship->rtid);
      if ($type->implies) {

        // if the type of the relationship we're inserting or updating implies other relationship type(s),
        // loop through the implied relationship types and do the right thing
        foreach ($type->implies as $implied) {

          // load all the pre-existing relationships between these two users of the implied relationship type
          $relationships = user_relationships_load(array(
            'between' => array(
              $relationship->requester_id,
              $relationship->requestee_id,
            ),
            'rtid' => $implied->implies_rtid,
          ));

          // if there aren't any, create one with the same approved status as the relationship we're inserting/updateing
          if (sizeof($relationships) == 0) {
            $users = array(
              $relationship->requester_id,
              $relationship->requestee_id,
            );
            if ($implied->reverse) {
              $users = array_reverse($users);
            }
            $implied = user_relationships_type_load($implied->implies_rtid);
            user_relationships_request_relationship($users[0], $users[1], $implied, $relationship->approved);
          }
          else {
            foreach ($relationships as $existing) {
              if ($relationship->approved && !$existing->approved) {

                // approve the relationship
                $approved = $existing;
                $approved->approved = TRUE;
                user_relationships_update_relationship($existing, $approved);

                // set the message informing the user (use requester and requestee from original relationship)
                drupal_set_message(user_relationships_get_message('accepted', $approved));
              }
            }
          }
        }
      }
      break;
  }
}

/**
 * hook_user_relationships_page_alter()
 */
function user_relationship_implications_user_relationships_page_alter($page_id, &$page, &$table) {
  switch ($page_id) {
    case 'types list':
      array_splice($table['headers'], 2, 0, t('Implies'));
      foreach ($table['data'] as $key => $relationship) {
        $relationship = user_relationships_type_load($relationship->rtid);
        array_splice($table['rows'][$key], 2, 0, '&nbsp;');
        $names = array();
        if (is_array($relationship->implies)) {
          foreach ($relationship->implies as $implied) {
            $implied = user_relationship_implication_load($implied->riid);
            $names[] = $implied->implies_relationship_type->name;
          }
        }
        $table['rows'][$key][2] = implode(', ', $names);
      }
      break;
  }
}

/**
 * Categorized list of relationships for a given user
 */
function theme_user_relationship_implications_page($uid = NULL, $relationship = NULL) {
  global $user;
  if (empty($uid)) {
    $viewed_user =& $user;
  }
  else {
    $viewed_user = user_load(array(
      'uid' => $uid,
    ));
  }

  // Check that the uid is valid, not the anonymous user, and the user exists
  if ($viewed_user->uid == 0) {
    drupal_not_found();
    exit;
  }
  $params = array(
    'user' => $viewed_user->uid,
  );
  if (isset($relationship->rtid)) {
    $params['rtid'] = $relationship->rtid;
  }
  $query = _user_relationships_generate_query($params);
  if ($relationships_per_page = variable_get('user_relationships_relationships_per_page', 16)) {
    $results = pager_query($query['query'], $relationships_per_page, 0, $query['count'], $query['arguments']);
  }
  else {
    $results = db_query($query['query'], $query['arguments']);
  }
  if (db_num_rows($results)) {
    $edit_access = $user->uid == $uid && user_access('maintain relationships') || user_access('administer users');
    $online_interval = time() - variable_get('user_block_seconds_online', 180);
    while ($relation = db_fetch_object($results)) {
      $this_user = $viewed_user->uid == $relation->requestee_id ? 'requester_id' : 'requestee_id';
      $this_user = user_load(array(
        'uid' => $relation->{$this_user},
      ));
      $relations = array();
      $this_users_relationships = user_relationships_load(array(
        'user' => $this_user->uid,
      ));
      $rows[] = array(
        theme('username', $this_user),
        theme('item_list', _user_relationship_implications_load_relationship_names($this_users_relationships, $viewed_user->uid)),
        $this_user->access > $online_interval ? t('online') : t('not online'),
        $edit_access ? theme('user_relationships_remove_link', $viewed_user->uid, $relation) : '&nbsp;',
      );
    }
    $output .= theme('table', array(), $rows);
  }
  else {
    $output .= t('No relationships found');
  }
  $output .= theme('pager', NULL, $relationships_per_page);
  drupal_set_title(t("%username's %relationships", array(
    '%username' => $viewed_user->name,
    '%relationships' => $relationship->plural_name ? $relationship->plural_name : t('relationships'),
  )));
  return $output;
}

/**
 * Theme function to create a table of checkboxes for the implications
 */
function theme_user_relationship_implications_form_table(&$form) {
  $headers = array(
    t('Relationship Type'),
    t('Strict'),
    t('Reverse'),
  );
  $rows = array();
  foreach ($form['opts'] as $rtid => $elements) {
    if (!is_numeric($rtid)) {
      continue;
    }
    $rows[$rtid] = array(
      drupal_render(array_shift($elements)),
      drupal_render(array_shift($elements)),
      drupal_render(array_shift($elements)),
    );
  }
  return theme('table', $headers, $rows);
}

/**
 * Helper functions (not for general use!!)
 */
function _user_relationship_implications_load_relationship_names($relationships, $uid) {
  $output = array();
  foreach ($relationships as $relationship) {
    if ($relationship->requester_id == $uid || $relationship->requestee_id == $uid) {
      $output[] = $relationship->name;
    }
  }
  return $output;
}
function _user_relationship_implications_filter_for_reverse($implication) {
  $implication = (array) $implication;
  return $implication['reverse'];
}

Functions

Namesort descending Description
theme_user_relationship_implications_form_table Theme function to create a table of checkboxes for the implications
theme_user_relationship_implications_page Categorized list of relationships for a given user
user_relationship_implications_edit_submit Edit relationship type submission processor
user_relationship_implications_form_alter hook_form_alter()
user_relationship_implications_load Public API to load all implied relationships
user_relationship_implications_user_relationships hook_user_relationships()
user_relationship_implications_user_relationships_page_alter hook_user_relationships_page_alter()
user_relationship_implication_load Public API to load an implied relationship
_user_relationship_implications_filter_for_reverse
_user_relationship_implications_load_relationship_names Helper functions (not for general use!!)