diff --git og_subgroups.admin.inc og_subgroups.admin.inc
new file mode 100644
index 0000000..21ab23d
--- /dev/null
+++ og_subgroups.admin.inc
@@ -0,0 +1,73 @@
+<?php
+
+/**
+ * @file
+ * Administration settings for OG Subgroups.
+ */
+
+/**
+ * Menu callback for admin settings form.
+ */
+function og_subgroups_settings($form, &$form_state) {
+  // Get default values from system variable.
+  $default_values = variable_get('og_subgroups_settings');
+  // Parent tree for Group selection.
+  $form['og_subgroups_settings'] = array(
+    '#type' => 'fieldset',
+    '#tree' => TRUE,
+    '#title' => t('Enable membership propagation for group types'),
+    '#description' => t("Select which available group types can have membership propagation enabled. If a certain group type already has membership propagation and you deselect it, it will not remove the hierarchy of those entities. They must be manually edited. Note: If both parent and children propagation are selected, group content will be subscribed to ALL groups connected to the content."),
+  );
+
+  // Build options array of groups content types.
+  foreach (og_get_all_group_bundle() as $group_type => $group_bundles) {
+    // If there aren't any group types display a warning.
+    if (empty($group_bundles)) {
+      $form['og_subgroups_settings']['#description'] = t('You currently have no group content types. You can add a group content type by visiting the !link page.', array('!link' => l(t('OG Field Settings'), 'admin/config/group/fields')));
+    }
+    // Otherwise show all group types and possible group content types.
+    else {
+      // Iterate over each group type.
+      foreach ($group_bundles as $group_bundle_name => $group_bundle_label) {
+        // Add a fieldset for each group type.
+        $form['og_subgroups_settings']['groups'][$group_type][$group_bundle_name] = array(
+          '#type' => 'fieldset',
+          '#title' => t('%bundle Group Content Propagation', array('%bundle' => $group_bundle_label)),
+          '#description' => t('For each entity type that can be associated with a %bundle group type, select how propagation will work for that entity type.', array('%bundle' => $group_bundle_label)),
+          '#collapsible' => TRUE,
+        );
+        // Build the user propagation selection element.
+        $form['og_subgroups_settings']['groups'][$group_type][$group_bundle_name]['user_propagation'] = array(
+          '#type' => 'checkbox',
+          '#title' => t('Sync user membership to all group content that is associated with %bundle group types.', array('%bundle' => $group_bundle_label)),
+          '#default_value' => isset($default_values['groups'][$group_type][$group_bundle_name]['user_propagation']) ? $default_values['groups'][$group_type][$group_bundle_name]['user_propagation'] : 0,
+        );
+
+        // Iterate over each possible group content types that can be associated with this group type.
+        foreach (og_get_all_group_content_bundle() as $group_content_type => $group_content_bundles) {
+          // Add a fieldset for each group content entity type.
+          $form['og_subgroups_settings']['groups'][$group_type][$group_bundle_name]['group_content'][$group_content_type] = array(
+            '#type' => 'fieldset',
+            '#title' => t('%type Entity Bundles', array('%type' => $group_content_type)),
+            '#collapsible' => TRUE,
+            '#collapsed' => TRUE,
+          );
+          // Iterate over each possible bundle for a given entity type.
+          foreach ($group_content_bundles as $group_content_bundle_name => $group_content_bundle_label) {
+            // Build the group content propagation selection element.
+            $form['og_subgroups_settings']['groups'][$group_type][$group_bundle_name]['group_content'][$group_content_type][$group_content_bundle_name] = array(
+              '#type' => 'checkboxes',
+              '#title' => t('%bundle content', array('%bundle' => $group_content_bundle_label)),
+              '#options' => array(
+                'parents' => t('Parents: A %group_content will be automatically subscribed to all parent groups of a %group group.', array('%group_content' => $group_content_bundle_label, '%group' => $group_bundle_label)),
+                'children' => t('Children: A %group_content will be automatically subscribed to all children groups of a %group group.', array('%group_content' => $group_content_bundle_label, '%group' => $group_bundle_label)),
+              ),
+              '#default_value' => isset($default_values['groups'][$group_type][$group_bundle_name]['group_content'][$group_content_type][$group_content_bundle_name]) ? $default_values['groups'][$group_type][$group_bundle_name]['group_content'][$group_content_type][$group_content_bundle_name] : array(),
+            );
+          }
+        }
+      }
+    }
+  }
+  return system_settings_form($form);
+}
\ No newline at end of file
diff --git og_subgroups.module og_subgroups.module
index f55ba1b..a345a3e 100755
--- og_subgroups.module
+++ og_subgroups.module
@@ -2,30 +2,17 @@
 
 /**
  * @file
- * Enable defining hierarchy of groups for organic groups.
+ * Enable group membership propagation for organic groups.
  */
 
 /**
- * Implementation of hook_ctools_plugin_directory().
- */
-function og_subgroups_ctools_plugin_directory($module, $plugin) {
-  // Safety: go away if CTools is not at an appropriate version.
-  if (!module_invoke('ctools', 'api_version', OG_REQUIRED_CTOOLS_API)) {
-    return;
-  }
-
-  if ($module == 'ctools') {
-    return 'plugins/' . $plugin;
-  }
-}
-/**
  * Check if a user has access permission in one of the ancestors groups
  * and return the tree structure of the group hierarchy.
  *
  * @param $entity_groups
  *    Array of group id to check and start moving up in the hierarchy.
  * @param $account
- *    Optional; The account to check
+ *    Optional; The account to check.
  * @param $string
  *    Optional; The permission string, if empty return the full tree structure in
  *    $structure, otherwise stops when the permission is grant.
@@ -39,49 +26,30 @@ function og_subgroups_ctools_plugin_directory($module, $plugin) {
  *    to change this. Default to 0 being the entity given.
  *
  *  @return
- *    TRUE if user has access grant with the given perm to one of the enstertors
+ *    TRUE if user has access grant with the given perm to one of the ancestor
  *    groups.
  */
-function og_subgroups_get_reverse_hierarchy_tree_perm($entity_groups, $string = '', $account = NULL, &$structure = array(), &$graph = FALSE , $level = 0) {
-  // Check if user has the permission in a parent group.
-  foreach($entity_groups as $gid) {
-    // Save the hierarchy structure.
-    $structure[$gid] = array();
-    $structure[$gid]['gid'] = $gid;
-    $structure[$gid]['level'] = $level;
-
-    // Build a graph with graph api
-    if (is_array($graph) && module_exists('graphapi')) {
-      $group = og_load($gid);
-      graphapi_set_node_title($graph, $gid,  $group->label);
-    }
-    // Check access permission.
+function og_subgroups_get_reverse_hierarchy_tree_perm($group_type, $gids, $string = '', $account = NULL, &$structure = array(), $level = 0) {
+  foreach ($gids as $gid) {
     if ($string) {
-      if (og_user_access($gid, $string, $account, TRUE)) {
+      // Check access permission.
+      if (og_user_access($group_type, $gid, $string, $account, TRUE)) {
         return TRUE;
       }
     }
   }
   // Get all groups that are content of user_groups (as an array of group ids).
-  $groups = og_load_multiple($entity_groups);
+  $groups = entity_load($group_type, $gids);
   $parent_groups = array();
-  foreach ($groups as $group) {
-    // Load the entity associated with the group.
-    $entity = og_load_entity_from_group($group->gid);
+  foreach ($groups as $entity) {
     // Get all groups that are associated with passed group.
-    $parents = og_get_entity_groups($group->entity_type, $entity);
-    $parent_groups += $parents;
-    // Build a graph path with graph api
-    if (is_array($graph) && module_exists('graphapi')) {
-      foreach ($parents as $parent) {
-        graphapi_set_node_title($graph, $parent,  $parent);
-        graphapi_set_link_data($graph, $group->gid, $parent, array('color' => '#018FE2'));
-      }
+    if ($parents = og_get_entity_groups($entity->type, $entity)) {
+      $parent_groups += $parents;
     }
   }
   if ($parent_groups) {
     // Recurssion call of the function.
-    return og_subgroups_get_reverse_hierarchy_tree_perm($parent_groups, $string, $account, $structure, $graph, ++$level);
+    return og_subgroups_get_reverse_hierarchy_tree_perm($group_type, $parent_groups, $string, $account, $structure, ++$level);
   }
   else {
     // Reached a dead end, return false.
@@ -90,118 +58,411 @@ function og_subgroups_get_reverse_hierarchy_tree_perm($entity_groups, $string =
 }
 
 /**
- * Implements hook_og_user_access_alter()
+ * Implements hook_og_user_access_alter().
+ *
+ * Update the permission for a user that tries to access a sub group.
+ * This gives to any users his og group permission to all his subgroups,
+ * without the need for him to be a member in the groups.
  */
 function og_subgroups_og_user_access_alter(&$perm, $context) {
-  // Update the permission for a user that tries to access a sub group.
-  // This gives to any users his og group permission to all his subgroups,
-  // without the -need for him to be a member in the groups.
-  $perm[$context['string']] = og_subgroups_get_reverse_hierarchy_tree_perm(array($context['group']->gid),  $context['string'], $context['account']);
+  $field_name = current(array_keys(og_get_group_audience_fields()));
+  $field = field_info_field($field_name);
+  $gids = array();
+  if (isset($context['group']->$field_name[LANGUAGE_NONE])) {
+    foreach ($context['group']->$field_name[LANGUAGE_NONE] as $delta) {
+      $gids[] = $delta['target_id'];
+    }
+  }
+  if (count($gids)) {
+    $perm[$context['string']] = og_subgroups_get_reverse_hierarchy_tree_perm($field['settings']['target_type'], $gids, $context['string'], $context['account']);
+  }
 }
 
+/**
+ * Implements hook_menu().
+ */
+function og_subgroups_menu() {
+  $items['admin/config/group/subgroups'] = array(
+    'title' => 'OG subgroups configuration',
+    'description' => 'Configure default behaviors of group membership propagation.',
+    'page callback' => 'drupal_get_form',
+    'page arguments' => array('og_subgroups_settings'),
+    'access arguments' => array('administer og subgroups.'),
+    'weight' => 0,
+    'file' => 'og_subgroups.admin.inc',
+  );
+  $items['admin/config/group/subgroups/settings'] = array(
+    'title' => 'Settings',
+    'weight' => 0,
+    'type' => MENU_DEFAULT_LOCAL_TASK,
+  );
+  return $items;
+}
+
+/**
+ * Implements hook_og_membership_insert().
+ */
+function og_subgroups_og_membership_insert($og_membership) {
+  og_subgroups_og_membership_propagate_create($og_membership);
+}
+
+/**
+ * Implements hook_og_membership_update().
+ */
+function og_subgroups_og_membership_update($og_membership) {
+  og_subgroups_og_membership_propagate_create($og_membership);
+}
+
+/**
+ * Implements hook_og_membership_delete().
+ */
+function og_subgroups_og_membership_delete($og_membership) {
+  og_subgroups_og_membership_propagate_delete($og_membership);
+}
 
 /**
- * Implements hook_node_access_records_alter().
+ * Propagates group membership to associated groups. Uses options set in the
+ * OG Subgroups administration page to determine propagation direction and
+ * entity type and bundle.
  *
- * This alter is fired on node save, we want to add view permission to a user's
- * subgroups (private groups), to do so we add the parent groups id to all
- * groups.
+ * @param OgMembership $og_membership
+ *   The OG membership that is being inserted or updated.
  */
-function og_subgroups_node_access_records_alter(&$grants, $node) {
-  // Relevant only for private groups.
-  if (module_exists('og_access')) {
-    // The group IDs, that in case access is granted, will be recorded.
-    $gids = array();
-    $private = FALSE;
-    $groups = array();
-    // Dealing with a node group that is private.
-    if (!empty($node->{OG_ACCESS_FIELD}[LANGUAGE_NONE][0]['value'])) {
-      $group = og_get_group('node', $node->nid);
-      if ($group) {
-        $groups[] = $group->gid;
-        $private = TRUE;
-      }
-    }
-    // Dealing with a group content.
-    elseif (isset($node->{OG_CONTENT_ACCESS_FIELD}[LANGUAGE_NONE][0]['value'])) {
-      // If no groups with og realm are defined, this means it's a public group
-      //  then do nothing, otherwise treat as a private group.
-      if (($node->{OG_CONTENT_ACCESS_FIELD}[LANGUAGE_NONE][0]['value'] == OG_CONTENT_ACCESS_PRIVATE) ||
-      ($node->{OG_CONTENT_ACCESS_FIELD}[LANGUAGE_NONE][0]['value'] == OG_CONTENT_ACCESS_DEFAULT && og_subgroups_grants_has_og_realm($grants))) {
-        $groups = og_get_entity_groups('node', $node);
-        $private = TRUE;
+function og_subgroups_og_membership_propagate_create($og_membership) {
+  // Extract entity info from group to get entity bundle. This way we can
+  // be sure to load the correct bundle name for all entity types.
+  $group = entity_load_single($og_membership->group_type, $og_membership->gid);
+  $group_info = entity_extract_ids($og_membership->group_type, $group);
+  $group_bundle = $group_info[2];
+
+  // Extract entity info from group content to get entity bundle. This way we can
+  // be sure to load the correct bundle name for all entity types.
+  $group_content = entity_load_single($og_membership->entity_type, $og_membership->etid);
+  $group_content_info = entity_extract_ids($og_membership->entity_type, $group_content);
+  $group_content_bundle = $group_content_info[2];
+
+  // Get settings for this group and group content type.
+  $settings = variable_get('og_subgroups_settings');
+  $propagation_setting = $settings['groups'][$og_membership->group_type][$group_bundle]['group_content'][$og_membership->entity_type][$group_content_bundle];
+
+  // Check if entity and bundle type should be propagated for this group type.
+  if ($propagation_setting['parents'] == 'parents' || $propagation_setting['children'] == 'children') {
+    // Get all groups associated with this membership gid.
+    $groups = og_subgroups_get_hierarchy($og_membership->group_type, $og_membership->gid, $propagation_setting);
+    // Iterate over groups and subscribe user to each group.
+    if (!empty($groups)) {
+      foreach ($groups as $type => $gids) {
+        foreach ($gids as $membership_id => $gid) {
+          // Build a membership relationship for the group and entity pair.
+          $new_membership = og_group($type, $gid, array('entity_type' => $og_membership->entity_type, 'entity' => $og_membership->etid));
+
+          // Don't allow entities to self-reference.
+          if ($new_membership->etid == $gid) {
+            continue;
+          }
+
+          // Save membership.
+          $new_membership->save();
+        }
       }
     }
-    // If group is private, then grant permissions for parent groups.
-    if ($private) {
-      og_subgroups_get_reverse_hierarchy_tree_perm($groups, '', NULL, $gids);
-      // Check existing grant and remove from gids[], to avoid duplication.
-      foreach ($grants as $granted) {
-        if (isset($gids[$granted['gid']])) {
-          unset($gids[$granted['gid']]);
+  }
+
+  // Get user propagation settings for this group type.
+  $og_membership_user_propagation_setting = $settings['groups'][$og_membership->group_type][$group_bundle]['user_propagation'];
+
+  // Check if users should be propagated for this group type and that the group content entity can also be a group type.
+  if ($og_membership_user_propagation_setting == TRUE && og_is_group_type($og_membership->entity_type, $group_content_bundle)) {
+    og_subgroups_og_membership_sync_users($og_membership);
+  }
+}
+
+/**
+ * Propagates deletion of group membership to associated groups. Uses options set in the
+ * OG Subgroups administration page to determine propagation direction and
+ * entity type and bundle.
+ *
+ * @param OgMembership $og_membership
+ *   The OG membership that is being inserted or updated.
+ */
+function og_subgroups_og_membership_propagate_delete($og_membership) {
+  // Load this group entity and extract entity info. This way we can be sure
+  // to load the correct bundle name for all entity types.
+  $group = entity_load_single($og_membership->group_type, $og_membership->gid);
+  $group_info = entity_extract_ids($og_membership->group_type, $group);
+  $group_bundle = $group_info[2];
+
+  // Load this group content entity and extract entity info. This way we can be sure
+  // to load the correct bundle name for all entity types.
+  $group_content = entity_load_single($og_membership->entity_type, $og_membership->etid);
+  $group_content_info = entity_extract_ids($og_membership->entity_type, $group_content);
+  $group_content_bundle = $group_content_info[2];
+
+  // Get settings for this group and group content type.
+  $settings = variable_get('og_subgroups_settings');
+  $propagation_setting = $settings['groups'][$og_membership->group_type][$group_bundle]['group_content'][$og_membership->entity_type][$group_content_bundle];
+
+  // Check if entity and bundle type should be propagated for this group type.
+  if ($propagation_setting['parents'] == 'parents' || $propagation_setting['children'] == 'children') {
+    // Get all groups associated with this membership gid.
+    $groups = og_subgroups_get_hierarchy($og_membership->group_type, $og_membership->gid, $propagation_setting);
+    // Iterate over groups and delete membership for each group.
+    if (!empty($groups)) {
+      foreach ($groups as $type => $gids) {
+        foreach ($gids as $membership_id => $gid) {
+          // Make sure this membership exists.
+          if ($old_membership = og_get_membership($type, $gid, $og_membership->entity_type, $og_membership->etid)) {
+            $old_membership->delete();
+          }
         }
       }
-      // Build the new access Grant array.
-      foreach ($gids as $gid) {
-        $grants[] = array (
-        'realm' => OG_ACCESS_AUTHENTICATED_REALM,
-        'gid' => $gid['gid'],
-        'grant_view' => 1,
-        'grant_update' => 0,
-        'grant_delete' => 0,
-        'priority' => 0,
-        );
-      }
     }
   }
 }
 
 /**
- * Return TRUE if $grants contain an OG realm
+ * Propagate user membership from a group entity to group content entity.
+ * The group content entity must also be a group entity. In order to reduce entity loads,
+ * the group content entity should be confirmed as a group before this function is called,
+ * using og_is_group_type().
+ *
+ * @param OgMembership $og_membership
+ *   The OG membership that is being inserted or updated.
  */
-function og_subgroups_grants_has_og_realm($grants) {
-  foreach ($grants as $granted) {
-    if ($granted['realm'] == OG_ACCESS_AUTHENTICATED_REALM) {
-      return TRUE;
+function og_subgroups_og_membership_sync_users($og_membership) {
+  // Get all user memberships for the current group.
+  $group_users = og_subgroups_get_users_group($og_membership->group_type, $og_membership->gid);
+  $entity_users = og_subgroups_get_users_group($og_membership->entity_type, $og_membership->etid);
+  if (!empty($entity_users['user'])) {
+    foreach ($entity_users['user'] as $uid) {
+      // Make sure this user membership doesn't already exist, since we are inserting directly.
+      if ( $this_membership = og_get_membership($og_membership->entity_type, $og_membership->etid, 'user', $uid) ) {
+        $this_membership->delete();
+      }
+    }
+  }
+  // Iterate over each user member of the group entity, and add a membership between the
+  // group content entity and the user.
+  if (!empty($group_users['user'])) {
+    foreach ($group_users['user'] as $uid) {
+      // Make sure this user membership doesn't already exist, since we are inserting directly.
+      if (!og_get_membership($og_membership->entity_type, $og_membership->etid, 'user', $uid)) {
+        // Build a membership relationship for the group and entity pair.
+        $field_name = current(array_keys(og_get_group_audience_fields()));
+        $new_membership = og_membership_create($og_membership->entity_type, $og_membership->etid, 'user', $uid, $field_name);
+
+        // Don't allow entities to self-reference. This would only be an issue if users can also be
+        // group entities.
+        if ($new_membership->gid == $uid) {
+          continue;
+        }
+        $new_membership->save();
+      }
     }
   }
-  return FALSE;
 }
 
 /**
- * Get hierarchy tree.
+ * Get group hierarchy tree.
  *
  * @param $entity_type
+ *   The entity type. Defaults to 'node'.
  * @param $etid
- * @param $options
+ *   The entity ID.
+ * @param $propagation_setting
+ *   Optional; Set in OG Subgroups admin configuration page.
+ * @param $tree
+ *   Optional; Referenced array that will store group tree information.
+ *
+ * @return
+ *  An array with the entities' entity type as the key, and array - keyed by
+ *  the OG membership ID and the Group GID Entity ID as the value. If nothing found,
+ *  then an empty array.
  */
-function og_get_hierarchy($entity_type, $etid, $options = array(), &$tree = array(), $depth = 0) {
-  $options += array(
-    'direction' => 'up',
-    'type' => 'single',
-    'sanitize' => TRUE,
-  );
+function og_subgroups_get_hierarchy($entity_type, $etid, $propagation_setting = array(), &$tree = array()) {
+  // Get all ancestor groups.
+  if (isset($propagation_setting['parents']) && $propagation_setting['parents'] === 'parents') {
+    // Use OG function to get this entities' group associations.
+    $parent_groups = og_get_entity_groups($entity_type, $etid);
+
+    // Iterate over groups array.
+    if (!empty($parent_groups)) {
+      foreach ($parent_groups as $type => $gids) {
+        foreach ($gids as $gid) {
+          // To avoid infinite recursion, we check that this gid hasn't been
+          // added to the $tree array already.
+          if (isset($tree[$type][$gid])) {
+            continue;
+          }
+          // Add group gid to $tree array.
+          $tree[$type][$gid] = $gid;
+          // Recursively call to function to check for more ancestor groups.
+          og_subgroups_get_hierarchy($type, $gid, $propagation_setting, $tree);
+        }
+      }
+    }
+  }
 
-  $wrapper = entity_metadata_wrapper($entity_type, $etid);
+  // Get all children groups.
+  if ((isset($propagation_setting['children']) && $propagation_setting['children'] === 'children') && !in_array($etid, $tree)) {
+    // Use OG Subgroups helper function to get this group's entity associations.
+    $children_entities = og_subgroups_get_group_entities($entity_type, $etid);
+    // Iterate over entities array.
+    if (!empty($children_entities)) {
+      foreach ($children_entities as $type => $etids) {
+        foreach ($etids as $etid) {
+          // Make sure we are only adding entities that are groups.
+          // TODO: og_is_group() may need to be changed to og_is_group_type().
+          if (og_is_group($type, $etid)) {
+            // To avoid infinite recursion, we check that this etid hasn't been
+            // added to the $tree array already.
+            if (isset($tree[$type][$etid])) {
+              continue;
+            }
 
-  if ($depth == 0 && $group = $wrapper->group->value()) {
-    if ($options['type'] == 'single') {
-      $tree[$group->gid] = og_label($group->gid, $options['sanitize']);
+            // Add entity id to $tree array.
+            $tree[$type][$etid] = $etid;
+            // Recursively call to function to check for more children entities.
+            og_subgroups_get_hierarchy($type, $etid, $propagation_setting, $tree);
+          }
+        }
+      }
     }
-    else {
-      $tree[$depth][$group->gid] = og_label($group->gid, $options['sanitize']);
+  }
+  return $tree;
+}
+
+/**
+ * Get users that are associated with a group.
+ *
+ * @param $entity_type
+ *   The entity type. Defaults to 'node'.
+ * @param $entity
+ *   Optional; The entity object or entity ID.
+ * @param $states
+ *   Optional; Array with the state to return. Defaults to active.
+ *
+ * @return
+ *  An array with the entities' entity type as the key, and array - keyed by
+ *  the OG membership ID and the Entity ID as the value. If nothing found,
+ *  then an empty array.
+ */
+function og_subgroups_get_users_group($entity_type = 'node', $entity = NULL, $states = array(OG_STATE_ACTIVE)) {
+  $cache = &drupal_static(__FUNCTION__, array());
+
+  if (is_object($entity)) {
+    // Get the entity ID.
+    list($id) = entity_extract_ids($entity_type, $entity);
+  }
+  else {
+    $id = $entity;
+  }
+
+  // Get a string identifier of the states, so we can retrieve it from cache.
+  if ($states) {
+    sort($states);
+    $state_identifier = implode(':', $states);
+  }
+  else {
+    $state_identifier = 0;
+  }
+
+  if (isset($cache[$entity_type][$id][$state_identifier])) {
+    // Return cached values.
+    return $cache[$entity_type][$id][$state_identifier];
+  }
+
+  $cache[$entity_type][$id][$state_identifier] = array();
+
+  $query = new EntityFieldQuery();
+  $query
+    ->entityCondition('entity_type', 'og_membership', '=')
+    ->propertyCondition('entity_type', 'user', '=')
+    ->propertyCondition('group_type', $entity_type, '=')
+    ->propertyCondition('gid', $id, '=');
+
+  if ($states) {
+    $query->propertyCondition('state', $states, 'IN');
+  }
+
+  $result = $query->execute();
+
+  if (!empty($result['og_membership'])) {
+    // Get the group ID from the group membership.
+    $og_memberships = og_membership_load_multiple(array_keys($result['og_membership']));
+    foreach ($og_memberships as $og_membership) {
+      $cache[$entity_type][$id][$state_identifier]['user'][$og_membership->id] = $og_membership->etid;
     }
   }
 
-  if ($options['direction'] == 'up' && $options['type'] == 'single') {
-    $group = FALSE;
-    // Get the first group associated with the entity.
-    if ($wrapper->og_membership->get(0)->value()) {
-      $group = $wrapper->og_membership->get(0)->group->value();
-      $tree[$group->gid] = og_label($group->gid, $options['sanitize']);
-      og_get_hierarchy($group->entity_type, $group->etid, $options, $tree, $depth + 1);
+  return $cache[$entity_type][$id][$state_identifier];
+}
+
+/**
+ * Get children entities that are associated with a group.
+ *
+ * @param $entity_type
+ *   The entity type. Defaults to 'user'.
+ * @param $entity
+ *   Optional; The entity object or entity ID. If empty, and $entity_type is
+ *   "user", the current user will be used.
+ * @param $states
+ *   Optional; Array with the state to return. Defaults to active.
+ *
+ * @return
+ *  An array with the entities' entity type as the key, and array - keyed by
+ *  the OG membership ID and the Entity ID as the value. If nothing found,
+ *  then an empty array.
+ */
+function og_subgroups_get_group_entities($entity_type = 'user', $entity = NULL, $states = array(OG_STATE_ACTIVE)) {
+  $cache = &drupal_static(__FUNCTION__, array());
+
+  if ($entity_type == 'user' && empty($entity)) {
+    global $user;
+    $entity = clone $user;
+  }
+  if (is_object($entity)) {
+    // Get the entity ID.
+    list($id) = entity_extract_ids($entity_type, $entity);
+  }
+  else {
+    $id = $entity;
+  }
+
+  // Get a string identifier of the states, so we can retrieve it from cache.
+  if ($states) {
+    sort($states);
+    $state_identifier = implode(':', $states);
+  }
+  else {
+    $state_identifier = 0;
+  }
+
+  if (isset($cache[$entity_type][$id][$state_identifier])) {
+    // Return cached values.
+    return $cache[$entity_type][$id][$state_identifier];
+  }
+
+  $cache[$entity_type][$id][$state_identifier] = array();
+
+  $query = new EntityFieldQuery();
+  $query->entityCondition('entity_type', 'og_membership', '=');
+  $query->propertyCondition('entity_type', $entity_type, '=');
+  $query->propertyCondition('gid', $id, '=');
+
+  if ($states) {
+    $query->propertyCondition('state', $states, 'IN');
+  }
+
+  $result = $query->execute();
+
+  if (!empty($result['og_membership'])) {
+    // Get the group ID from the group membership.
+    $og_memberships = og_membership_load_multiple(array_keys($result['og_membership']));
+    foreach ($og_memberships as $og_membership) {
+      $cache[$entity_type][$id][$state_identifier][$og_membership->group_type][$og_membership->id] = $og_membership->etid;
     }
   }
 
-  return $tree;
+  return $cache[$entity_type][$id][$state_identifier];
 }
diff --git og_subgroups.test og_subgroups.test
index 31129d4..48bddd9 100644
--- og_subgroups.test
+++ og_subgroups.test
@@ -1,5 +1,9 @@
 <?php
-// $Id$
+
+/**
+ * @file
+ * Testing functions for OG Subgroups.
+ */
 
 /**
  * Test the Organic groups subgroups API.
@@ -14,19 +18,17 @@ class OgSubgroupsAccessFromDescendanceTestCase extends DrupalWebTestCase {
   }
 
   function setUp() {
-    parent::setUp('og_subgroups', 'entity_test','og_access');
+    parent::setUp('og_subgroups', 'entity_test', 'og_access');
     // Add OG group field to the entity_test's "main" bundle.
     og_create_field(OG_GROUP_FIELD, 'entity_test', 'main');
     og_create_field(OG_AUDIENCE_FIELD, 'entity_test', 'main');
     og_create_field(OG_AUDIENCE_FIELD, 'article', 'main');
   }
-  
-  
 
   /**
    * Use cases:
-   * - Check that parent user has access to all permission he has on groups he 
-   * is not memberof but that are descandents of his group
+   * - Check that parent user has access to all permission he has on groups he
+   * is not member of but that are descendents of his group.
    */
   function testOgSubgroupsGetReverseHierarchyTreePerm() {
     // Create user.
@@ -36,7 +38,7 @@ class OgSubgroupsAccessFromDescendanceTestCase extends DrupalWebTestCase {
     $user4 = $this->drupalCreateUser();
     $web_user = $this->drupalCreateUser(array('create article content', 'create page content'));
     $this->drupalLogin($web_user);
-    
+
     $groups = array();
 
     // Create an entity that is a group.
@@ -44,28 +46,27 @@ class OgSubgroupsAccessFromDescendanceTestCase extends DrupalWebTestCase {
     $entity->{OG_GROUP_FIELD}[LANGUAGE_NONE][0]['value'] = 1;
     $entity->save();
     $groups[0] = og_get_group('entity_test', $entity->pid);
-    
-   // Create a private entity group that is a child group of the group above.
+
+    // Create a private entity group that is a child group of the group above.
     $entity = entity_create('entity_test', array('name' => 'first_child', 'uid' => $user2->uid));
     $entity->{OG_GROUP_FIELD}[LANGUAGE_NONE][0]['value'] = 1;
     $entity->{OG_AUDIENCE_FIELD}[LANGUAGE_NONE][0]['gid'] =  $groups[0]->gid;
     $entity->save();
     $groups[1] = og_get_group('entity_test', $entity->pid);
-    
+
     // Create an entity that is a child group of the group above.
     $entity = entity_create('entity_test', array('name' => 'second_child', 'uid' => $user3->uid));
     $entity->{OG_GROUP_FIELD}[LANGUAGE_NONE][0]['value'] = 1;
     $entity->{OG_AUDIENCE_FIELD}[LANGUAGE_NONE][0]['gid'] =  $groups[1]->gid;
     $entity->save();
     $groups[2] = og_get_group('entity_test', $entity->pid);
-    
+
     // Create an entity that is a group content of group ID 2.
-  
     $node = $this->drupalCreateNode(array('type' => 'article', 'promote' => 1));
     $node->{OG_AUDIENCE_FIELD}[LANGUAGE_NONE][0]['gid'] = $groups[2]->gid;
-    node_save($node);    
+    node_save($node);
     $groups[3] = $node;
-    
+
     $group_content[0] = $entity;
 
     // Associate user4 to the group[2].
@@ -73,47 +74,49 @@ class OgSubgroupsAccessFromDescendanceTestCase extends DrupalWebTestCase {
 
     // Assert the user is registered to the new group.
     $string = 'administer group';
+
     // Check user1 has adminster perm on parent group [0]
     debug($groups);
     $this->assertTrue(og_user_access($groups[0]->gid, $string, $user1, TRUE) , t('User1 have admister group permission to parent group 0'));
+
     // Check user has admin perm on sub group that is he not member of.
     $access_res = og_subgroups_get_reverse_hierarchy_tree_perm(array($groups[2]->gid), $string, $user1);
     $this->assertTrue($access_res, t('User1 have admister group permission to subgroup 2'));
 
     // Check permission.
     $this->assertFalse(og_user_access($groups[0]->gid, $string, $user3), t('User3 does not have "Administer group" permission.'));
+
     // Check user3 has no adminster perm on parent group [0]
     $access_res = og_subgroups_get_reverse_hierarchy_tree_perm(array($groups[0]->gid), $string, $user1);
     $this->assertFalse($access_res, t('User3 does not have admister group permission to group 0'));
 
     // Change permissions to authenticated member.
-    //$roles = array_flip(og_get_global_roles());
+    // $roles = array_flip(og_get_global_roles());
     // Authenticated role ID.
-   // $rid = $roles[OG_AUTHENTICATED_ROLE];
+    // $rid = $roles[OG_AUTHENTICATED_ROLE];
 
-   // $permissions = array(
-  //    'delete own article content' => 1,
-  ///    'administer group' => 1,
-  //  );
-  //  og_role_change_permissions($rid, $permissions);
+    // $permissions = array(
+    //    'delete own article content' => 1,
+    ///    'administer group' => 1,
+    //  );
+    //  og_role_change_permissions($rid, $permissions);
 
     // Verify proper permission changes.
-   // $this->assertFalse(og_user_access($group->gid, 'update own article content', $user2), t('User still does not have "update own article content" permission.'));
-   // $this->assertTrue(og_user_access($group->gid, 'delete own article content', $user2), t('User now has "delete own article content" permission.'));
-  //  $this->assertTrue(og_user_access($group->gid, 'administer group', $user2), t('User now has "administer group" permission.'));
+    // $this->assertFalse(og_user_access($group->gid, 'update own article content', $user2), t('User still does not have "update own article content" permission.'));
+    // $this->assertTrue(og_user_access($group->gid, 'delete own article content', $user2), t('User now has "delete own article content" permission.'));
+    //  $this->assertTrue(og_user_access($group->gid, 'administer group', $user2), t('User now has "administer group" permission.'));
 
-   // $permissions = array(
-   //   'delete own article content' => 1,
-   //   'administer group' => 0,
-  //  );
- //  og_role_change_permissions($rid, $permissions);
+    // $permissions = array(
+    //   'delete own article content' => 1,
+    //   'administer group' => 0,
+    //  );
+    //  og_role_change_permissions($rid, $permissions);
 
-  //  $this->assertTrue(og_user_access($group->gid, 'delete own article content', $user2), t('User still has "delete own article content" permission.'));
-  //  $this->assertFalse(og_user_access($group->gid, 'administer group', $user2), t('User no longer has "administer group" permission.'));
+    //  $this->assertTrue(og_user_access($group->gid, 'delete own article content', $user2), t('User still has "delete own article content" permission.'));
+    //  $this->assertFalse(og_user_access($group->gid, 'administer group', $user2), t('User no longer has "administer group" permission.'));
 
   }
-  
-  
+
   /**
    * Test the view access of child node.
    * Use cases:
@@ -121,7 +124,7 @@ class OgSubgroupsAccessFromDescendanceTestCase extends DrupalWebTestCase {
    *  access), that he is not part of.
    * 2. Anonymous user tries to view private child groups node (with public
    *  access).
-   * 
+   *
    */
   function testOgSubgroupsNodeAccessRecordsAlter() {
     // Create user.
@@ -131,7 +134,7 @@ class OgSubgroupsAccessFromDescendanceTestCase extends DrupalWebTestCase {
     $user4 = $this->drupalCreateUser();
     $web_user = $this->drupalCreateUser(array('create article content', 'create page content'));
     $this->drupalLogin($web_user);
-    
+
     $groups = array();
 
     // Create an entity that is a group.
@@ -140,15 +143,15 @@ class OgSubgroupsAccessFromDescendanceTestCase extends DrupalWebTestCase {
     $entity->save();
     $groups[0]['group'] = og_get_group('entity_test', $entity->pid);
     $groups[0]['entity'] = $entity;
-    
-   // Create an entity that is a child group of the group above (0).
+
+    // Create an entity that is a child group of the group above (0).
     $entity = entity_create('entity_test', array('name' => 'first_child', 'uid' => $user2->uid));
     $entity->{OG_GROUP_FIELD}[LANGUAGE_NONE][0]['value'] = 1;
     $entity->{OG_AUDIENCE_FIELD}[LANGUAGE_NONE][0]['gid'] =  $groups[0]['group']->gid;
     $entity->save();
     $groups[1]['group'] = og_get_group('entity_test', $entity->pid);
     $groups[1]['entity'] = $entity;
-    
+
     // Create a private entity group that is a child group of the group above. (1).
     $entity = entity_create('entity_test', array('name' => 'second_child', 'uid' => $user3->uid));
     $entity->{OG_GROUP_FIELD}[LANGUAGE_NONE][0]['value'] = 1;
@@ -157,24 +160,22 @@ class OgSubgroupsAccessFromDescendanceTestCase extends DrupalWebTestCase {
     $entity->save();
     $groups[2]['group'] = og_get_group('entity_test', $entity->pid);
     $groups[2]['entity'] = $entity;
-    
+
     // Create an entity that is a group content of group ID 2.
     $node = $this->drupalCreateNode(array('type' => 'article', 'promote' => 1));
     $node->{OG_AUDIENCE_FIELD}[LANGUAGE_NONE][0]['gid'] = $groups[2]['group']->gid;
-    node_save($node); 
+    node_save($node);
     $groups[3]['node'] = $node;
-    
-    
 
     $this->drupalLogout();
+
     // Assert the anonymous can't view the node.
     $this->drupalGet('node/' . $groups[3]['node']->nid);
     $this->assertResponse('403', t('Annonymous cannot view private node.'));
-    
+
     $this->drupalLogin($user4);
     $this->drupalGet('node/' . $groups[3]['node']->nid);
     $this->assertResponse('403', t('Not group member or parrent cannot view private node.'));
-    
 
     // Assert another user is not a group member.
     $this->drupalLogin($user1);
@@ -182,5 +183,5 @@ class OgSubgroupsAccessFromDescendanceTestCase extends DrupalWebTestCase {
     $this->assertResponse('200', t('Parent group user can view the node'));
 
   }
-  
+
 }
