diff -urpN old/og_hierarchy/og_hierarchy.info new/og_hierarchy/og_hierarchy.info
--- old/og_hierarchy/og_hierarchy.info	1970-01-01 05:30:00.000000000 +0530
+++ new/og_hierarchy/og_hierarchy.info	2008-05-02 19:01:56.078125000 +0530
@@ -0,0 +1,8 @@
+; $Id:
+
+name = Oragnic groups hierarchy
+description = "Enable defining hierarchy of groups for organic groups."
+package = "Organic groups"
+dependencies = og
+
+
diff -urpN old/og_hierarchy/og_hierarchy.install new/og_hierarchy/og_hierarchy.install
--- old/og_hierarchy/og_hierarchy.install	1970-01-01 05:30:00.000000000 +0530
+++ new/og_hierarchy/og_hierarchy.install	2008-05-05 23:19:47.437500000 +0530
@@ -0,0 +1,40 @@
+<?php
+// $Id
+
+function og_hierarchy_install() {
+  switch ($GLOBALS['db_type']) {
+    case 'mysql':
+    case 'mysqli':
+      db_query("CREATE TABLE {og_hierarchy} (
+        gid int(11) NOT NULL,
+        parent int(11) NOT NULL,
+        PRIMARY KEY (gid, parent)
+      ) /*!40100 DEFAULT CHARACTER SET utf8 */;");
+    break;
+    case 'pgsql':
+      db_query("
+      CREATE TABLE {og_ancestry} (
+       gid int NOT NULL,
+       parent int NOT NULL,
+       PRIMARY KEY (gid, parent)
+      );");
+      db_query("CREATE INDEX {og_hierarchy}_gid_idx ON {og_hierarchy} (gid);");
+      db_query("CREATE INDEX {og_hierarchy}_parent_idx ON {og_hierarchy} (parent);");
+      break;
+  }
+}
+
+
+function og_hierarchy_uninstall() {
+  // Drop database table
+  db_query('DROP TABLE {og_hierarchy}');
+
+  // Delete variables
+  //TODO: Add variables.
+  $variables = array(
+    'og_hierarcy_propagte_post',
+  );
+  foreach ($variables as $variable) {
+    variable_del($variable);
+  }
+}
diff -urpN old/og_hierarchy/og_hierarchy.module new/og_hierarchy/og_hierarchy.module
--- old/og_hierarchy/og_hierarchy.module	1970-01-01 05:30:00.000000000 +0530
+++ new/og_hierarchy/og_hierarchy.module	2008-05-06 16:06:30.890625000 +0530
@@ -0,0 +1,358 @@
+<?php
+// $Id: og_subgroups.module,v 1.26 2008/03/18 04:24:09 ezrag Exp $
+
+/**
+ * @file
+ * Maintains a hierarchy of groups created by the orgainc groups module.
+ */
+
+/**
+ * Implementation of hook_perm().
+ */
+function og_hierarchy_perm() {
+    return array('administer groups hierachy', 'view groups hierarchy', 'edit groups hierarchy');
+}
+
+/**
+ * Implementation of hook_menu().
+ */
+function og_hierarchy_menu($may_cache) {
+  $items = array();
+  if (!$may_cache) {
+    $items[] = array(
+      'path' => 'admin/og/hierarchy',
+      'title' => t('Organic groups hierarchy configuration'),
+      'callback' => 'drupal_get_form', 
+      'callback arguments' => array('og_hierarchy_settings'),
+      'access' => user_access('administer groups hierachy'),
+      'weight' => 1,
+     );
+    
+    $arg0 = arg(0);
+    $arg1 = arg(1);
+    if ($arg0 == 'node' && is_numeric($arg1)) {
+      $node = node_load($arg1);
+      if (og_is_group_type($node->type)) {
+        $items[] = array(
+          'path' => 'node/'. arg(1) .'/og/hierarchy',
+          'title' => t('Group hierarchy'),
+          'callback' => 'drupal_get_form',
+          'callback arguments' => array('og_hierarchy_outline', arg(1)),
+          'access' => user_access('edit groups hierarchy'),
+          'type' => MENU_LOCAL_TASK,
+          'weight' => 2);
+      }
+    }
+  }
+  return $items;
+}
+
+
+function og_hierarchy_set_breadcrumb($addchild = false) {
+//TODO: implement
+/*  $gnode = og_get_group_context();
+  $parent = _og_hierarchy_get_parent($gnode->nid);
+  if ($parent) {
+    $pnode = node_load($parent);
+    $bc[] = l(t('Home'), NULL);
+    $bc[] = l(t('Groups'), 'og');
+    $bc[] = l($pnode->title, "node/$pnode->nid");
+  if ($addchild)
+    $bc[] = l($gnode->title, "node/$gnode->nid");
+    drupal_set_breadcrumb($bc);
+  }*/
+}
+
+/**
+ * Menu callback; displays the groups hierarchy configuration page.
+ */
+function og_hierarchy_settings() {
+  $form['propagte'] = array(
+    '#type' => 'fieldset',
+    '#title' => t('Propogation'),
+    '#description' => t('Content and users can be propagated through the hierarchy tree.'),
+  );
+    $form['propagte']['og_hierarcy_propagte_post'] = array(
+    '#type' => 'checkboxes',
+    '#title' => t('Posts propagation'),
+    '#description' => t('Set the propogation type.'),
+    '#options' => array(
+      'parents' => t('Parents'),
+      'children' => t('Children'),
+      'syblings' => t('Syblings'),
+    ),
+    '#default_value' => variable_get('og_hierarcy_propagte_post', NULL),
+  );
+  
+
+  return system_settings_form($form);
+}
+
+
+/**
+ * Implementation of hook_block().
+ *
+ * Displays the book table of contents in a block when the current page is a
+ * single-node view inside a group context.
+ */
+function og_hierarchy_block($op = 'list', $delta = 0) {
+  $block = array();
+  if ($op == 'list') {
+    $block[0]['info'] = t('Group hierarchy');
+    return $block;
+  }
+  else if ($op == 'view') {
+    // Only display this block when the user is browsing a book:
+    $arg = arg(1);
+    if (arg(0) == 'node' && is_numeric($arg)) {
+      $node = og_hierarchy_get_parent($arg);
+      if ($node->gid) {
+        $path = og_hierarchy_location($arg);
+        $path[] = $node;
+      }
+      else {
+        // Top level group.
+        $node = node_load($arg);
+        $path[] = (object) array('gid' => $node->nid, 'title' => $node->title);
+      }
+        $expand = array();
+        foreach ($path as $key => $node) {
+          $expand[] = $node->gid;
+        }
+        
+        $block['subject'] = t('Group hierarchy');
+        $block['content'] = og_hierarchy_menu_tree($expand[0], $path[0]->title, 5);
+      
+    }
+
+    return $block;
+  }
+}
+
+/**
+ * Given a node, this function returns an array of 'group node' objects
+ * representing the path in the group hierarchy from the root to the
+ * parent of the given node.
+ *
+ * @param $node
+ *   A group node object for which to compute the path.
+ *
+ * @return
+ *   An array of group node objects representing the path nodes root to
+ *   parent of the given node. Returns an empty array if the node does
+ *   not exist or is not part of a groups hierarchy.
+ */
+function og_hierarchy_location($gid, $nodes = array()) {
+  $parent = og_hierarchy_get_parent($gid);
+  if ($parent->gid) {
+    $nodes = og_hierarchy_location($parent->gid, $nodes);
+    $nodes[] = $parent;
+  }
+  return $nodes;
+}
+
+/**
+ * Implementation of hook_nodeapi().
+*/
+function og_hierarchy_nodeapi($node, $op, $teaser = NULL, $page = NULL) {
+  switch ($op) {
+    case 'load':
+      if (og_is_group_type($node->type)) {
+        $parent = og_hierarchy_get_parent($node->nid);
+        $node->og_hierarchy = $parent->gid? $parent->gid : NULL;
+      }
+      break;
+  }
+}
+
+/**
+ * Get the parent for a given group node id.
+ * 
+ * @param $gid
+ *  The group id
+ * 
+ * @return 
+ *  The parent group id
+ */
+function og_hierarchy_get_parent($gid) {
+  //TODO: Make nicer...
+  $parent_gid = "SELECT ogh.parent FROM {node} n INNER JOIN {og_hierarchy} ogh WHERE n.nid = ogh.gid AND n.nid = %d";
+  $parent_title = "SELECT n.title FROM {node} n INNER JOIN {og_hierarchy} ogh WHERE n.nid = ogh.parent AND n.nid = %d";
+  $result_gid = db_fetch_object(db_query($parent_gid, $gid));
+  $result_title = db_fetch_object(db_query($parent_title, $result_gid->parent));
+  $return = (object)array('gid' => $result_gid->parent, 'title' => $result_title->title);
+  return $return;
+}
+
+/**
+ * Implementation of function book_outline()
+ * Handles all book outline operations.
+ */
+function og_hierarchy_outline($nid) {
+  $node = node_load($nid);
+  // If user doesn't have access to all groups, show only groups she's member.
+  
+  $form['parent'] = array(
+    '#type' => 'select',
+    '#title' => t('Parent'),
+    '#default_value' => $node->og_hierarchy,
+    '#options' => og_hierarchy_tree($node->nid, og_all_groups_options()),
+    '#description' => t('The parent group for this group node in the book.'),
+  );
+  $form['log'] = array(
+    '#type' => 'textarea',
+    '#title' => t('Log message'),
+    '#description' => t('An explanation to help other group admins understand your motivations to change the groups hierarchy.'),
+  );
+  $form['nid'] = array(
+    '#type' => 'value', 
+    '#value' => $nid,
+  );
+  
+  if (isset($node->og_hierarchy)) {
+    $form['update'] = array(
+      '#type' => 'submit',
+      '#value' => t('Update group hierarchy'),
+    );
+    $form['remove'] = array(
+      '#type' => 'submit',
+      '#value' => t('Remove group from hierarchy'),
+    );
+  }
+  else {
+    $form['add'] = array(
+      '#type' => 'submit', 
+      '#value' => t('Set group hierarchy'),
+    );
+  }
+
+  drupal_set_title(check_plain($node->title));
+  return $form;
+}
+
+/**
+ * Handles group hierarchy form submissions.
+ */
+function og_hierarchy_outline_submit($form_id, $form_values) {
+  $op = $form_values['op'];
+  $node = node_load($form_values['nid']);
+  // TODO: Use API.
+  switch ($op) {
+    case t('Set group hierarchy'):
+      if ($form_values['parent']) {
+        db_query('INSERT INTO {og_hierarchy} (gid, parent) VALUES (%d, %d)', $node->nid, $form_values['parent']);
+        db_query("UPDATE {node_revisions} SET log = '%s' WHERE vid = %d", $form_values['log'], $node->vid);
+      }
+      drupal_set_message(t('The group hierarchy has been set.'));
+      break;
+    case t('Update group hierarchy'):
+      if ($form_values['parent']) {
+        db_query('UPDATE {og_hierarchy} SET parent = %d WHERE gid = %d', $form_values['parent'], $node->nid);
+        db_query("UPDATE {node_revisions} SET log = '%s' WHERE vid = %d", $form_values['log'], $node->vid);
+      } 
+      else {
+        db_query('DELETE FROM {og_hierarchy} WHERE gid = %d', $node->nid);
+      }
+      drupal_set_message(t('The group hierarchy has been updated.'));
+      break;
+    case t('Remove group from hierarchy'):
+      db_query('DELETE FROM {og_hierarchy} WHERE gid = %d', $node->nid);
+      drupal_set_message(t('Group has been removed from hierarchy.'));
+      break;
+  }
+  //TODO: return "node/$node->nid";
+}
+
+/**
+ * Returns an array of titles and nid entries of groups in tree hiearachy order.
+ */
+function og_hierarchy_tree($exclude = 0, $groups = array()) {
+  $children = array();
+  // Get all children of eligable groups, if any.
+  foreach ($groups as $key => $group) {
+    $parent = og_hierarchy_get_parent($key); 
+    if ($parent->gid) {
+      $children[$parent->gid][] = (object)array('gid' => $key, 'title' => $group);
+    }
+    else {
+      // Group is top level.
+      $children[0][] = (object)array('gid' => $key, 'title' => $group);
+    }
+  }
+  
+  $tree = array();
+  $tree[0] = '<'. t('top-level') .'>';
+  
+  $tree = og_hierarchy_tree_recurse(0, '', $tree, $children, $exclude);
+  return $tree;
+}
+
+/**
+ * Helper function for og_hierarchy_tree().
+ */
+function og_hierarchy_tree_recurse($gid, $indent, $tree, $children, $exclude) {
+  if ($children[$gid]) {
+    foreach ($children[$gid] as $foo => $node) {
+      if (!$exclude || $exclude != $node->gid) {
+        $tree[$node->gid] = $indent .' '. $node->title;
+        $tree = og_hierarchy_tree_recurse($node->gid, $indent .'--', $tree, $children, $exclude);
+      }
+    }
+  }
+
+  return $tree;
+}
+
+
+/**
+ * Returns an HTML nested list (wrapped in a menu-class div) representing the group nodes
+ * as a tree.
+ */
+function og_hierarchy_menu_tree($parent = 0, $parent_title='', $depth = 3) {
+  $result = db_query(db_rewrite_sql('SELECT n.nid, n.title, ogh.parent FROM {node} n INNER JOIN {og_hierarchy} ogh ON n.nid = ogh.gid WHERE n.status = 1 ORDER BY n.title'));
+  $children = array();
+  // Set the top level also as child
+  $children[0][$parent] = (object) array(
+    'nid' => $parent,
+    'title' => $parent_title,
+    'parent' => 0,    
+  );  
+  while ($node = db_fetch_object($result)) {
+    $list = isset($children[$node->parent]) ? $children[$node->parent] : array();
+    $list[$node->nid] = $node;
+    $children[$node->parent] = $list;
+  }
+
+  if ($tree = og_hierarchy_menu_tree_recurse($parent, $depth, $children)) {
+    $output = '<li class="expanded">';
+    $output .= '<ul class="menu">'. l(check_plain($parent_title), 'node/'. $parent) .'</ul>';
+    $output .= '</li>';
+    $output .= '<ul class="menu">'. $tree .'</ul>';
+  
+    return $output;
+  }
+}
+
+/**
+ * Helper function for book_tree()
+ */
+function og_hierarchy_menu_tree_recurse($nid, $depth, $children) {
+  $output = '';
+  if ($depth > 0) {
+    if (isset($children[$nid])) {
+      foreach ($children[$nid] as $foo => $node) {
+        if ($tree = og_hierarchy_menu_tree_recurse($node->nid, $depth - 1, $children)) {
+          $output .= '<li class="expanded">';
+          $output .= l($node->title, 'node/'. $node->nid);
+          $output .= '<ul class="menu">'. $tree .'</ul>';
+          $output .= '</li>';
+        }
+        else {
+          $output .= '<li class="leaf">'. l($node->title, 'node/'. $node->nid) .'</li>';
+        }
+      }
+    }
+  }
+  return $output;
+}
+
