From 2e1d1e077f4380b157d6c03df8b270b07d843fdd Mon Sep 17 00:00:00 2001
From: Caio SBA <caiosba@gmail.com>
Date: Fri, 26 Jul 2013 18:47:50 -0300
Subject: [PATCH] Implementing optional two-way inheritance with support to propagation

---
 og_subgroups.common.inc |  150 +++++++++++++++++++++++++++++++++++++++++------
 og_subgroups.module     |   37 +++++++-----
 2 files changed, 153 insertions(+), 34 deletions(-)

diff --git a/og_subgroups.common.inc b/og_subgroups.common.inc
index f228eed..90d6988 100644
--- a/og_subgroups.common.inc
+++ b/og_subgroups.common.inc
@@ -6,6 +6,24 @@
  */
 
 /**
+ * Get children groups.
+ */
+function _og_subgroups_get_children($group_type, $group_id, &$children_groups) {
+  $children = og_subgroups_get_associated_entities($group_type, $group_id);
+  foreach ($children as $children_type => $children_ids) {
+    $children_entities = entity_load($children_type, $children_ids);
+    foreach ($children_entities as $child_id => $child_entity) {
+      if (og_is_group($children_type, $child_entity)) {
+        if (isset($child_entity->{OG_USER_INHERITANCE_FIELD}[LANGUAGE_NONE]) && in_array(array('value' => OG_USER_INHERITANCE_FIELD_PARENT), $child_entity->{OG_USER_INHERITANCE_FIELD}[LANGUAGE_NONE])) {
+          $children_groups[$children_type . '-' . $child_id] = array('type' => $children_type, 'id' => $child_id);
+          _og_subgroups_get_children($children_type, $child_id, $children_groups);
+        }
+      }
+    }
+  }
+}
+
+/**
  * Get parent groups.
  *
  * This functions works by traversing the tree from the current
@@ -246,7 +264,7 @@ function og_subgroups_get_associated_entities($entity_type, $entity) {
 }
 
 /**
- * Return an array of inherited users from the parent groups that
+ * Return an array of inherited users from the parent and children groups that
  * allow for user inheritance.
  *
  * @param $entity_type
@@ -265,21 +283,21 @@ function og_subgroups_get_associated_entities($entity_type, $entity) {
  */
 function _og_subgroups_get_inherited_users($group_type, $group_id, $states = array(OG_STATE_ACTIVE)) {
   $inherited_users = array();
-  $parents = _og_subgroups_get_unique_parents($group_type, $group_id);
-  foreach ($parents as $parent) {
-    $users = og_subgroups_get_users_group($parent['type'], $parent['id'], $states);
+  $groups = _og_subgroups_get_unique_parents($group_type, $group_id) + _og_subgroups_get_unique_children($group_type, $group_id);
+  foreach ($groups as $group) {
+    $users = og_subgroups_get_users_group($group['type'], $group['id'], $states);
     foreach ($users['user'] as $membership_id => $uid) {
-      $roles = og_get_user_roles($parent['type'], $parent['id'], $uid);
+      $roles = og_get_user_roles($group['type'], $group['id'], $uid);
       $membership_ids[] = $membership_id;
       $user_ids[] = $uid;
-      $inherited_from[$parent['type']][] = $parent['id'];
+      $inherited_from[$group['type']][] = $group['id'];
       $inherited_users[$uid][] = array(
         'uid' => $uid,
         'membership_id' => $membership_id,
         'roles' => $roles,
         'inherited_from' => array(
-          'type' => $parent['type'],
-          'id' => $parent['id'],
+          'type' => $group['type'],
+          'id' => $group['id'],
         )
       );
     }
@@ -303,31 +321,56 @@ function _og_subgroups_get_inherited_users($group_type, $group_id, $states = arr
 }
 
 /**
- * Return an array of all the parent groups, optionally filtered
- * including only the groups that allow for inheritance.
+ * Return an array of all the children groups.
+ *
+ * @param $group_type
+ *   The group type.
+ * @param $group_id
+ *   The group ID.
+ *
+ * @return
+ *  An array with the entities' entity type and entity id as the key, and array
+ *  containing the entity type, entity id, and entity object as values.
+ *  If nothing is found, returns an empty array.
+ */
+function _og_subgroups_get_unique_children($group_type, $group_id) {
+  $children_groups = array();
+  _og_subgroups_get_children($group_type, $group_id, $children_groups);
+  return $children_groups;
+}
+
+/**
+ * Return an array of all the parent groups.
+ * We stop propagation when we find a group that does not allow inheritance.
  *
  * @param $group_type
  *   The group type.
  * @param $group_id
  *   The group ID.
- * @param $filter
- *   Boolean value for whether to return only groups that allow for inheritance.
  *
  * @return
  *  An array with the entities' entity type and entity id as the key, and array
  *  containing the entity type, entity id, and entity object as values.
  *  If nothing is found, returns an empty array.
  */
-function _og_subgroups_get_unique_parents($group_type, $group_id, $filter = TRUE) {
+function _og_subgroups_get_unique_parents($group_type, $group_id) {
   $groups = array();
+  $paths = array();
   og_subgroups_get_parents($group_type, $group_id, $paths);
   og_subgroups_populate_paths($paths);
   foreach ($paths as $path) {
-    foreach ($path as $group) {
+    $continue = TRUE;
+    $i = 0;
+    while($continue && $i < count($path)) {
+      $group = $path[$i];
       $entity = $group['entity'];
-      if (!$filter || (isset($entity->{OG_USER_INHERITANCE_FIELD}) && $entity->{OG_USER_INHERITANCE_FIELD}[LANGUAGE_NONE][0]['value'] == 1)) {
+      if (isset($entity->{OG_USER_INHERITANCE_FIELD}[LANGUAGE_NONE]) && in_array(array('value' => OG_USER_INHERITANCE_FIELD_SUBGROUP), $entity->{OG_USER_INHERITANCE_FIELD}[LANGUAGE_NONE])) {
         $groups[$group['type'] . '-' . $group['id']] = $group;
       }
+      else {
+        $continue = FALSE;
+      }
+      $i++;
     }
   }
   return $groups;
@@ -348,18 +391,22 @@ function _og_subgroups_get_unique_parents($group_type, $group_id, $filter = TRUE
  */
 function _og_subgroup_get_user_inherited_groups($account) {
   $children_groups = array();
+  $parent_groups = array();
   $groups = og_get_entity_groups('user', $account);
   if (!empty($groups)) {
     foreach ($groups as $group_type => $gids) {
       foreach ($gids as $gid => $group_id) {
         $group_entity = current(entity_load($group_type, array($group_id)));
-        if (isset($group_entity->{OG_USER_INHERITANCE_FIELD}[LANGUAGE_NONE][0]['value']) && $group_entity->{OG_USER_INHERITANCE_FIELD}[LANGUAGE_NONE][0]['value']) {
+        if (isset($group_entity->{OG_USER_INHERITANCE_FIELD}[LANGUAGE_NONE]) && in_array(array('value' => OG_USER_INHERITANCE_FIELD_SUBGROUP), $group_entity->{OG_USER_INHERITANCE_FIELD}[LANGUAGE_NONE])) {
           _og_subgroups_get_children_groups($group_type, $group_id, $children_groups);
         }
+        if (isset($group_entity->{OG_USER_INHERITANCE_FIELD}[LANGUAGE_NONE]) && in_array(array('value' => OG_USER_INHERITANCE_FIELD_PARENT), $group_entity->{OG_USER_INHERITANCE_FIELD}[LANGUAGE_NONE])) {
+          _og_subgroups_get_parent_groups($group_type, $group_id, $parent_groups);
+        }
       }
     }
   }
-  return $children_groups;
+  return _og_subgroup_merge_groups($children_groups, $parent_groups);
 }
 
 /**
@@ -381,10 +428,75 @@ function _og_subgroups_get_children_groups($group_type, $group_id, &$children_gr
     foreach ($children_entities as $child_id => $child_entity) {
       if (og_is_group($children_type, $child_entity)) {
         $children_groups[$children_type][$child_id] = $child_entity;
-        if (isset($child_entity->{OG_USER_INHERITANCE_FIELD}[LANGUAGE_NONE][0]['value']) && $child_entity->{OG_USER_INHERITANCE_FIELD}[LANGUAGE_NONE][0]['value']) {
+        if (isset($child_entity->{OG_USER_INHERITANCE_FIELD}[LANGUAGE_NONE]) && in_array(array('value' => OG_USER_INHERITANCE_FIELD_SUBGROUP), $child_entity->{OG_USER_INHERITANCE_FIELD}[LANGUAGE_NONE])) {
           _og_subgroups_get_children_groups($children_type, $child_id, $children_groups);
         }
       }
     }
   }
-}
\ No newline at end of file
+}
+
+/**
+ * Helper function for _og_subgroup_get_user_inherited_groups().
+ * We stop propagation when we find a group that does not allow inheritance.
+ *
+ * @param $group_type
+ *   The group type.
+ * @param $group_id
+ *   The group ID.
+ * @param $parent_groups
+ *   An array of parent groups.
+ *
+ * @see: _og_subgroup_get_user_inherited_groups().
+ */
+function _og_subgroups_get_parent_groups($group_type, $group_id, &$parent_groups) {
+  $paths = array();
+  $groups = array();
+  og_subgroups_get_parents($group_type, $group_id, $paths);
+  og_subgroups_populate_paths($paths);
+  foreach ($paths as $path) {
+    $continue = TRUE;
+    $i = 0;
+    $previous = current(entity_load($group_type, array($group_id)));
+    while($continue && $i < count($path)) {
+      $group = $path[$i];
+      $entity = $group['entity'];
+      if (isset($previous->{OG_USER_INHERITANCE_FIELD}[LANGUAGE_NONE]) && in_array(array('value' => OG_USER_INHERITANCE_FIELD_PARENT), $previous->{OG_USER_INHERITANCE_FIELD}[LANGUAGE_NONE])) {
+        $parent_groups[$group['type']][$group['id']] = $group;
+        $previous = $entity;
+      }
+      else {
+        $continue = FALSE;
+      }
+      $i++;
+    }
+  }
+}
+
+/**
+ * Helper function for _og_subgroup_get_user_inherited_groups().
+ * Merge arrays of children and parent groups.
+ * 'array_merge', 'array_merge_recursive' or '+' give wrong results.
+ *
+ * @param $array,...
+ *   Any number of arrays to be merged.
+ *
+ * @see: _og_subgroup_get_user_inherited_groups().
+ */
+function _og_subgroup_merge_groups() {
+  $merged = array();
+  $args = func_get_args();
+  foreach ($args as $arg) {
+    if (is_array($arg)) {
+      foreach ($arg as $type => $group) {
+        if (!isset($merged[$type])) {
+          $merged[$type] = array();
+        }
+        foreach ($group as $id => $obj) {
+          $merged[$type][$id] = $obj;
+        }
+      }
+    }
+  }
+  return $merged;
+}
diff --git a/og_subgroups.module b/og_subgroups.module
index 4d57056..bc26ac7 100644
--- a/og_subgroups.module
+++ b/og_subgroups.module
@@ -12,39 +12,46 @@ require drupal_get_path('module', 'og_subgroups') . '/og_subgroups.common.inc';
  * Group default roles and permissions field.
  */
 define('OG_USER_INHERITANCE_FIELD', 'og_user_inheritance');
+define('OG_USER_INHERITANCE_FIELD_SUBGROUP', 1);
+define('OG_USER_INHERITANCE_FIELD_PARENT', 2);
 
 /**
  * Implements hook_og_fields_info().
  */
 function og_subgroups_og_fields_info() {
   $allowed_values = array(
-    0 => 'No - subgroups of this group won\'t inherit its users.',
-    1 => 'Yes - subgroups of this group will inherit its users.',
+    OG_USER_INHERITANCE_FIELD_SUBGROUP => t('Subgroups of this group will inherit its users.'),
+    OG_USER_INHERITANCE_FIELD_PARENT => t('Parents of this group will inherit its users.'),
   );
   $items[OG_USER_INHERITANCE_FIELD] = array(
     'type' => array('group'),
-    'description' => t('Determine if the subgroups of a group will inherit its users.'),
+    'description' => t('Determine if the subgroups of a group will inherit its users and if the parents of a group will inherit its users.'),
     'entity' => array('node'),
     'field' => array(
       'field_name' => OG_USER_INHERITANCE_FIELD,
       'no_ui' => TRUE,
-      'type' => 'list_boolean',
-      'cardinality' => 1,
+      'type' => 'list_integer',
+      'cardinality' => -1,
       'settings' => array('allowed_values' => $allowed_values, 'allowed_values_function' => ''),
     ),
     'instance' => array(
       'label' => t('Group user inheritance'),
-      'required' => TRUE,
-      'default_value' => array(0 => array('value' => 1)),
-      'widget_type' => 'options_select',
+      'required' => FALSE,
+      'default_value' => NULL,
+      'widget' => array(
+        'type' => 'options_buttons',
+        'module' => 'options',
+      ),
       'view modes' => array(
         'full' => array(
           'label' => 'above',
-          'type' => 'options_onoff',
+          'module' => 'options',
+          'type' => 'options_buttons',
         ),
         'teaser' => array(
           'label' => 'above',
-          'type' => 'options_onoff',
+          'module' => 'options',
+          'type' => 'options_buttons',
         ),
       ),
     ),
@@ -94,14 +101,14 @@ function og_subgroups_og_user_access_alter(&$perms, $context) {
   $group_type = $context['group_type'];
   $group = $context['group'];
   list($id) = entity_extract_ids($group_type, $group);
-  $parent_groups = _og_subgroups_get_unique_parents($group_type, $id);
-  if (!empty($parent_groups)) {
-    foreach ($parent_groups as $parent_group) {
+  $groups = _og_subgroups_get_unique_parents($group_type, $group_id) + _og_subgroups_get_unique_children($group_type, $group_id);
+  if (!empty($groups)) {
+    foreach ($groups as $group) {
       // Recursion is prevented by $skip_alter = TRUE.
-      if (og_user_access($parent_group['type'], $parent_group['id'], $context['string'], $context['account'], TRUE)) {
+      if (og_user_access($group['type'], $group['id'], $context['string'], $context['account'], TRUE)) {
         $perms[$context['string']] = TRUE;
         break;
       }
     }
   }
-}
\ No newline at end of file
+}
-- 
1.7.2.5

