Updated version that provides the means to overload the method called. This was requested by edewata to support services. I also simplified some of the internal code .

https://fedorahosted.org/freeipa/attachment/ticket/104/admiyo-freeipa-0019-2-associations.patch
>From b04111d9068838e44c4c5b6e69d2d9845af46423 Mon Sep 17 00:00:00 2001
From: Adam Young <ayo...@redhat.com>
Date: Fri, 3 Sep 2010 10:55:20 -0400
Subject: [PATCH] associations

    -Refactored the associations code into a set of objects that are configured by the entities
    -Added support for associations that can be done in a single rpc
    -hostgroup to host and group to user associations working

    -Restructed sampledata so that the file is matched automatically by the RPC method name
    -The new ipa_cmd/sampledata scheme insists on there being sample data for any commands or the ipa_command fails.
    -Added sampledata files for all the calls we make
    -renamed several of the sampledata files to match their rpc calls

    -Started a pattern of refactoring where all the  forms for the entity fall under a single object
---
 install/static/Makefile.am                         |    1 +
 install/static/associate.js                        |  256 ++++++++++++++++++++
 install/static/details.js                          |   12 +-
 install/static/group.js                            |   59 +++--
 install/static/host.js                             |   16 +-
 install/static/hostgroup.js                        |   80 +++++--
 install/static/index.xhtml                         |   31 ++--
 install/static/ipa.js                              |   14 +-
 install/static/navigation.js                       |   37 +++-
 install/static/netgroup.js                         |    5 +-
 install/static/sampledata/group_add.json           |   31 +++
 install/static/sampledata/group_add_member.json    |   25 ++
 .../sampledata/{grouplist.json => group_find.json} |    0
 .../sampledata/{groupshow.json => group_show.json} |    0
 install/static/sampledata/host_add.json            |   40 +++
 .../sampledata/{hostlist.json => host_find.json}   |    0
 .../sampledata/{hostshow.json => host_show.json}   |    0
 install/static/sampledata/hostgroup_add.json       |   27 ++
 .../static/sampledata/hostgroup_add_member.json    |   37 +++
 .../{hostgrouplist.json => hostgroup_find.json}    |    0
 .../{hostgroupshow.json => hostgroup_show.json}    |    7 +-
 .../{metadata.json => json_metadata.json}          |    0
 install/static/sampledata/netgroup_add.json        |   28 +++
 .../{netgrouplist.json => netgroup_find.json}      |    0
 .../{netgroupshow.json => netgroup_show.json}      |    0
 install/static/sampledata/user_add.json            |   50 ++++
 .../sampledata/{userlist.json => user_find.json}   |    0
 .../sampledata/{usershow.json => user_show.json}   |    0
 install/static/search.js                           |  110 ++++-----
 install/static/user.js                             |  168 ++------------
 30 files changed, 743 insertions(+), 291 deletions(-)
 create mode 100644 install/static/associate.js
 create mode 100644 install/static/sampledata/group_add.json
 create mode 100644 install/static/sampledata/group_add_member.json
 rename install/static/sampledata/{grouplist.json => group_find.json} (100%)
 rename install/static/sampledata/{groupshow.json => group_show.json} (100%)
 create mode 100644 install/static/sampledata/host_add.json
 rename install/static/sampledata/{hostlist.json => host_find.json} (100%)
 rename install/static/sampledata/{hostshow.json => host_show.json} (100%)
 create mode 100644 install/static/sampledata/hostgroup_add.json
 create mode 100644 install/static/sampledata/hostgroup_add_member.json
 rename install/static/sampledata/{hostgrouplist.json => hostgroup_find.json} (100%)
 rename install/static/sampledata/{hostgroupshow.json => hostgroup_show.json} (65%)
 rename install/static/sampledata/{metadata.json => json_metadata.json} (100%)
 create mode 100644 install/static/sampledata/netgroup_add.json
 rename install/static/sampledata/{netgrouplist.json => netgroup_find.json} (100%)
 rename install/static/sampledata/{netgroupshow.json => netgroup_show.json} (100%)
 create mode 100644 install/static/sampledata/user_add.json
 rename install/static/sampledata/{userlist.json => user_find.json} (100%)
 rename install/static/sampledata/{usershow.json => user_show.json} (100%)

diff --git a/install/static/Makefile.am b/install/static/Makefile.am
index 366975e..63e5894 100644
--- a/install/static/Makefile.am
+++ b/install/static/Makefile.am
@@ -3,6 +3,7 @@ NULL =
 appdir = $(IPA_DATA_DIR)/static
 app_DATA =                              \
 	add.js				\
+	associate.js			\
 	but-reset.png			\
 	but-update.png			\
 	but-selected.png		\
diff --git a/install/static/associate.js b/install/static/associate.js
new file mode 100644
index 0000000..dabc76c
--- /dev/null
+++ b/install/static/associate.js
@@ -0,0 +1,256 @@
+/*  Authors:
+ *    Adam Young <ayo...@redhat.com>
+ *
+ * Copyright (C) 2010 Red Hat
+ * see file 'COPYING' for use and warranty information
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; version 2 only
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/* IPA Object Add  - creating new instances of entities */
+
+/* REQUIRES: ipa.js */
+
+function keyForFacet(tab, facet){
+    qs = ipa_parse_qs();
+    var key = qs['tab'] +'-'+ qs['facet'];
+    return key;
+}
+
+/**
+*This associator is built for the case where each association requires a separate rpc
+*/
+function SerialAssociator(form, oneObjPkey, manyObjPkeys){
+    this.form = form;
+    this.manyObjPkeys =  manyObjPkeys;
+    this.oneObjPkey = oneObjPkey;
+
+    this.associateNext = function(){
+        var form = this.form;
+        //TODO assert pre-conditions
+        var  manyObjPkey =  manyObjPkeys.shift();
+        if (manyObjPkey){
+            var options = {};
+            options[form.oneObj] = oneObjPkey;
+            var args = [manyObjPkey];
+            var associator = this;
+
+            ipa_cmd( form.method,args, options ,
+                     function(){
+                         associator.associateNext();
+                     },
+                     function(response){
+                         alert("associateFailure");
+                     },
+                     form.manyObj );
+        }else{
+            location.hash="tab="+form.oneObj
+                +"&facet=details&pkey="+this.oneObjPkey;
+        }
+    }
+
+}
+
+/**
+*This associator is for the common case where all the asociations can be sent 
+in a single rpc
+*/
+function BulkAssociator(form, pkey, manyObjPkeys){
+
+ this.form = form;
+    this.pkey =pkey;
+    this.manyObjPkeys =  manyObjPkeys;
+
+    this.associateNext = function(){
+        var form = this.form;
+        var option = manyObjPkeys.shift();
+        while(manyObjPkeys.length > 0){
+            option += "," + manyObjPkeys.shift();
+        }
+
+        var options = {
+          "all":true
+        };
+        options[form.manyObj] = option;
+
+        var args = [this.pkey];
+
+        ipa_cmd( form.method,args, options ,
+                 function(response){
+                     var qs = ipa_parse_qs();
+                     if (response.error){
+                         alert("error adding memeber");
+                     }else{
+                         location.hash="tab=" +form.oneObj
+                             +"&facet=details&pkey="+this.pkey;
+                     }
+                 },
+                 function(response){
+                     alert("associateFailure");
+                 },
+                 form.oneObj );
+    }
+}
+
+/**
+ *  Create a form for a one to many association.
+ *
+ */
+function AssociationForm(oneObj, manyObj,facet,facets, searchColumn, headerText , associatorConstructor, method){
+    this.oneObj = oneObj;
+    this.manyObj = manyObj;
+    this.facet = facet;
+    this.facets = facets;
+    this.headerText = headerText;
+    this.searchColumn = searchColumn;
+    //An optional parameter to determine what ipa method to call to create 
+    //the association
+    if (method){
+        this.method = method;
+    }else{
+        this.method = 'add_member';
+    }
+
+    if (associatorConstructor){
+        this.associatorConstructor = associatorConstructor;
+    }else{
+        this.associatorConstructor = SerialAssociator;
+    }
+
+    this.setup = function(pkey){
+        showAssociations();
+        qs = ipa_parse_qs();
+        $("#availableList").html("");
+        $("#enrollments").html("");
+
+        setupFacetNavigation(this.oneObj,qs['pkey'],qs['facet'],this.facets);
+
+        this.currentUserToEnroll = qs['pkey'];
+        this.manyObjPkeys = [];
+        var form = this;
+
+        $('h1').text(this.headerText());
+
+
+        $("#enroll").click(function(){
+            form.associate();
+        });
+        $("#addToList").click(function(){
+            $('#availableList :selected').each(function(i, selected){
+                $("#enrollments").append(selected);
+            });
+            $('#availableList :selected').remove();
+        });
+        $("#removeFromList").click(function(){
+            $('#enrollments :selected').each(function(i, selected){
+                $("#availableList").append(selected);
+            });
+            $('#enrollments :selected').remove();
+        });
+        $("#find").click(function(){
+            form.search();
+        });
+    }
+    this.search = function(){
+
+        var queryFilter = $("#associateFilter").val();
+
+        var form = this;
+        ipa_cmd( 'find', [queryFilter], {}, 
+                 function(searchResults){
+                        form.populateSearch(searchResults);
+                 },
+                 function(){
+                     alert("associationSearchFailure");
+                 },
+                 this.manyObj);
+    }
+
+    this.associate = function(){
+        var manyObjPkeys =  [];
+        $('#enrollments').children().each(function(i, selected){
+            manyObjPkeys.push(selected.value);
+        });
+        var pkey = qs['pkey'];
+        var associator = 
+            new this.associatorConstructor (this, pkey, manyObjPkeys);
+        associator.associateNext();
+    }
+    this.populateSearch = function(searchResults){
+        var results = searchResults.result;
+        $("#availableList").html("");
+        for (var i =0; i != results.count; i++){
+            var result = results.result[i];
+            $("<option/>",{
+                value: result[this.searchColumn][0],
+                html:  result[this.searchColumn][0]
+            }).appendTo( $("#availableList"));
+        }
+    }
+}
+
+
+/** 
+    A modfied version of search. It shows the  associations for an object.
+*/
+function AssociationList(obj,facet,assignFacet,associationColumns,facets) {
+    this.obj = obj;
+    this.facet = facet;
+    this.assignFacet = assignFacet;
+    this.associationColumns = associationColumns;
+    this.facets = facets;
+
+
+    this.populate = function(userData){
+        var associationList = userData.result.result[this.associationColumns[0].column];
+        for (var j = 0; j < associationList.length; j++){
+            var row  = $("<tr/>").appendTo($('#searchResultsTable thead:last'));
+            for (var k = 0; k < associationColumns.length ;k++){
+                var column = this.associationColumns[k].column;
+                $("<td/>",{
+                    html: userData.result.result[column][j]
+                }).appendTo(row);
+            }
+        }
+    }
+    this.setup=function(){
+        qs = ipa_parse_qs();
+        showSearch();
+        buildFacetNavigation(facets);
+        $("#filter").css("display","none");
+        $("#searchButtons").html("");
+        $("<input/>",{
+            type:  'button',
+            value: 'enroll',
+            click: function(){
+                location.hash="tab="+obj+"&facet="+assignFacet+"&pkey="+qs['pkey'];
+            }
+        }).appendTo("#searchButtons");
+        var header = $("<tr/>").appendTo($('#searchResultsTable thead:last'));
+        for (var i =0 ; i != associationColumns.length ;i++){
+            $("<th/>",{
+                html: associationColumns[i].title
+            }).appendTo(header);
+        }
+        var form = this;
+        ipa_cmd( 'show', [qs['pkey']], {},
+                 function(result){
+                     form.populate(result);
+                 },
+                 function(){
+                     alert("associationListFailure");
+                 },
+                 this.obj);
+    }
+}
diff --git a/install/static/details.js b/install/static/details.js
index 40d36b6..e291b49 100644
--- a/install/static/details.js
+++ b/install/static/details.js
@@ -40,7 +40,7 @@ var _ipa_load_on_fail_callback = null;
 
 var ipa_details_cache = null;
 
-function ipa_details_load(pkey, on_win, on_fail,sampleData)
+function ipa_details_load(pkey, on_win, on_fail)
 {
     if (!pkey)
         return;
@@ -50,7 +50,7 @@ function ipa_details_load(pkey, on_win, on_fail,sampleData)
 
     ipa_cmd(
         'show', [pkey], {all: true}, _ipa_load_on_win, _ipa_load_on_fail,
-        _ipa_obj_name, sampleData );
+        _ipa_obj_name);
 }
 
 function _ipa_load_on_win(data, text_status, xhr)
@@ -439,12 +439,12 @@ function _h2_on_click(obj)
     }
 }
 
-function DetailsForm(obj, details_list, pkeyCol, sampleData   ){
+function DetailsForm(obj, details_list, pkeyCol,  facets ){
 
     this.obj = obj;
     this.details_list = details_list;
-    this.sampleData = sampleData;
     this.pkeyCol = pkeyCol;
+    this.facets = facets;
 
     this.setup= function(key){
         //re initialize global parse of parameters
@@ -452,9 +452,11 @@ function DetailsForm(obj, details_list, pkeyCol, sampleData   ){
 
         showDetails();
         $('h1').text("Managing " + this.obj +": " +qs['pkey'] );
+        setupFacetNavigation(this.obj,qs.pkey,qs.facet,this.facets);
+
 
         ipa_details_init(this.obj);
         ipa_details_create(this.details_list, $('#details'));
-        ipa_details_load(qs.pkey, on_win, null, this.sampleData);
+        ipa_details_load(qs.pkey, on_win, null);
     }
 }
diff --git a/install/static/group.js b/install/static/group.js
index 981e048..8c5491a 100644
--- a/install/static/group.js
+++ b/install/static/group.js
@@ -1,10 +1,13 @@
 function setupGroup(facet){
-    if (facet == "details"){
-    setupGroupDetails();
-    }else  if (facet == "add"){
-    setupAddGroup();
+
+    if (groupForms[facet]){
+        groupForms[facet].setup();
+    }else if (facet == "details"){
+        setupGroupDetails();
+    }else if (facet == "add"){
+        setupAddGroup();
     }else{
-    groupSearchForm.setup();
+        groupForms.search.setup();
     }
 }
 
@@ -74,33 +77,26 @@ var group_details_list =
     ['description', 'Description'],
     ['gidnumber', 'Group ID']]]];
 
+var groupFacets=['details','users'];
+
 function setupGroupDetails(group){
 
     //re initialize global parse of parameters
     qs = ipa_parse_qs();
 
     showDetails();
-
+    setupFacetNavigation('group',qs['pkey'],qs['facet'],groupFacets);
     ipa_details_init('group');
     ipa_details_create(group_details_list, $('#details'));
-    ipa_details_load(qs['pkey'], on_win, null, "sampledata/groupshow.json");
+    ipa_details_load(qs['pkey'], on_win, null);
     $('h1').text('Managing group: ' + group);
 }
 
-
-
-function renderGroupDetails(group)
-{
-
-}
-
-
 function renderGroupDetailColumn(current,cell){
 
     $("<a/>",{
-    href:"#tab=group&facet=details&pkey="+current.cn,
-    html:  ""+ current[this.column],
-    //click: function(){ setupGroupDetails(current.cn)},
+        href:"#tab=group&facet=details&pkey="+current.cn,
+        html:  ""+ current[this.column],
     }).appendTo(cell);
 }
 
@@ -112,7 +108,32 @@ var groupSearchColumns = [
     {title:"Description",  column:"description",render: renderSimpleColumn}
 ];
 
-var groupSearchForm = new SearchForm("group", "find", groupSearchColumns ,"sampledata/grouplist.json");
+var groupForms = new GroupForms();
+
+function GroupForms(){
+
+    this.userListColumns = [ {title:"user",column:"member_user" }];
+    this.obj="group";
+    this.users = new AssociationList(
+        this.obj,
+        "users",
+        "assignusers",
+        this.userListColumns, groupFacets );
+
+    this.assignusers = new AssociationForm(
+        this.obj,
+        "user",
+        "assignusers",
+        groupFacets,
+        "uid",
+        function(){
+            return 'Add Users to group : '  + qs['pkey'] ;
+        },
+        BulkAssociator);
 
 
+    this.search =  new SearchForm("group", "find", groupSearchColumns );
+
+
+}
 
diff --git a/install/static/host.js b/install/static/host.js
index b249c9b..dba4f35 100644
--- a/install/static/host.js
+++ b/install/static/host.js
@@ -1,10 +1,10 @@
 function setupHost(facet){
     if (facet == "details"){
-    hostDetailsForm.setup();
+        hostDetailsForm.setup();
     }else if (facet == "add"){
-    hostBuilder.setup();
+        hostBuilder.setup();
     }else{
-    hostSearchForm.setup();
+        hostSearchForm.setup();
     }
 }
 
@@ -17,11 +17,13 @@ var host_details_list =  [['host', 'Host Details', [
     ['krbprincipalname', 'Kerberos Principal'],
     ['serverhostname', 'Server Host Name']
 ]]];
- 
-var hostDetailsForm = new DetailsForm("host",host_details_list,"fqdn","sampledata/hostshow.json") ;
 
+var hostFacets = ["details","hostgroup", "hostgroupmembership"];
 
-var hostDetailsColumns = [
+var hostDetailsForm = new DetailsForm("host",host_details_list,"fqdn",
+                                      hostFacets ) ;
+
+var hostSearchColumns = [
     {title:"Host",column:"fqdn",render: function(current,cell){
     renderPkeyColumn(hostDetailsForm,current,cell);
     }},
@@ -29,4 +31,4 @@ var hostDetailsColumns = [
     {title:"Enrolled?",  render: renderUnknownColumn},
     {title:"Manages?",   render: renderUnknownColumn}
 ];
-var hostSearchForm = new SearchForm("host", "find", hostDetailsColumns,"sampledata/hostlist.json");
+var hostSearchForm = new SearchForm("host", "find", hostSearchColumns);
diff --git a/install/static/hostgroup.js b/install/static/hostgroup.js
index 290460d..65732a1 100644
--- a/install/static/hostgroup.js
+++ b/install/static/hostgroup.js
@@ -1,11 +1,6 @@
+
 function setupHostgroup(facet){
-    if (facet == "details"){
-        hostgroupDetailsForm.setup();
-    }else if (facet == "add"){
-        hostgroupBuilder.setup();
-    }else{
-        hostgroupSearchForm.setup();
-    }
+        hostgroupForms.setup(facet);
 }
 
 var hostgroup_details_list =
@@ -13,8 +8,7 @@ var hostgroup_details_list =
         ['cn', 'Hostgroup Name'],
         ['description', 'Description']]]];
 
-
-var hostgroupDetailsForm = new DetailsForm("hostgroup",hostgroup_details_list,"cn","sampledata/hostgroupshow.json") ;
+var hostgroupFacets = ["details","hosts","assignhosts"];
 
 
 
@@ -26,23 +20,63 @@ function hostgroupAddOptionsFunction (){
     return options;
 }
 
-var hostgroupAddProperties =
+var hostgroupForms = new HostgroupsForms();
+
+function HostgroupsForms(){
+
+    this.setup = function(facet){
+        if (this[facet]){
+            this[facet].setup();
+        }else{
+            this.unspecified.setup();
+        }
+    }
+
+
+/**
+*  used to initialize the search
+*/
+    this.hostgroupSearchColumns = [
+        {
+            title:"Hostgroup",
+            column:"cn",
+            render:  function(current,cell){
+                renderPkeyColumn2('hostgroup', 'cn', current,cell);
+            }
+        },
+        {title:"Description", column:"description",render: renderSimpleColumn}];
+
+    this.hostgroupAddProperties =
     [{title: 'Hostgroup Name', id: 'pkey', type: 'text'},
      {title: 'Description', id: 'description', type: 'text'}];
 
-var hostgroupBuilder = new EntityBuilder("hostgroup",hostgroupAddProperties,hostgroupAddOptionsFunction);
 
+    /**
+       Facets
+    */
+    this.hostListColumns = [ {title:"Host",column:"member_host" }];
+    this.obj="hostgroup";
+    this.hosts = new AssociationList(
+        this.obj,
+        "hosts",
+        "assignhosts", 
+        this.hostListColumns, hostgroupFacets );
 
-var hostgroupSearchColumns = [
-    {
-        title:"Hostgroup", 
-        column:"cn", 
-        render:  function(current,cell){
-            renderPkeyColumn(hostgroupDetailsForm, current,cell);
-        }
-    },
-    {title:"Description", column:"description",render: renderSimpleColumn}];
+    this.assignhosts = new AssociationForm(
+        this.obj,
+        "host",
+        "assignhosts",
+        hostgroupFacets,
+        "fqdn",
+        function(){
+            return 'Add Hosts to to  hostgroup : '  + qs['pkey'] ;
+        },
+        BulkAssociator);
 
-var hostgroupSearchForm = 
-    new SearchForm("hostgroup", "find", hostgroupSearchColumns,
-                   "sampledata/hostgrouplist.json");
+    this.details = new DetailsForm("hostgroup",hostgroup_details_list,"cn",hostgroupFacets) ;
+
+    this.add = new EntityBuilder("hostgroup",this.hostgroupAddProperties,hostgroupAddOptionsFunction);
+
+    this.search = new SearchForm("hostgroup", "find", this.hostgroupSearchColumns);
+    this.unspecified = this.search;
+}
diff --git a/install/static/index.xhtml b/install/static/index.xhtml
index 8e231f4..4077d4f 100644
--- a/install/static/index.xhtml
+++ b/install/static/index.xhtml
@@ -12,6 +12,7 @@
     <script type="text/javascript" src="search.js"></script>
     <script type="text/javascript" src="details.js"></script>
     <script type="text/javascript" src="add.js"></script>
+    <script type="text/javascript" src="associate.js"></script>
     <script type="text/javascript" src="user.js"></script>
     <script type="text/javascript" src="usermeta.js"></script>
     <script type="text/javascript" src="group.js"></script>
@@ -23,10 +24,7 @@
     <script type="text/javascript">
 
       $(document).ready(function(){
-      if (useSampleData)
-        ipa_init("sampledata/metadata.json", buildNavigation);
-      else
-        ipa_init(null, buildNavigation);
+        ipa_init( buildNavigation);
       });
 
     </script>
@@ -46,11 +44,16 @@
       <div id="sub-nav">Edit: </div>
     </div>
 
+    <h1></h1>
+    <ul id="viewtype" style="display:none" >
+      <li id="viewcaption"></li>
+    </ul>
+
+
     <div id="content" border="1"  style="display:none" >
     </div>
 
     <div id="details" style="display:none" >
-      <h1>Managing user:</h1>
       <div id="buttons">
 	<a href="jslink" onclick="return (reset_on_click(this));">
           <img id="butreset" src="but-reset.png" alt="Reset" />
@@ -60,9 +63,6 @@
 	</a>
       </div>
 
-      <ul id="viewtype">
-	<li id="viewcaption">View:</li>
-      </ul>
       <div id="detail-lists">
 	<hr />
       </div>
@@ -89,24 +89,23 @@
     </div>
 
     <div id="associations" style="display:none">
-      <h1>Enroll in Groups</h1>
       <ul id="viewtype">
 	<li id="viewcaption">View:</li>
       </ul>
       <form>
 	<div style="border-width:1px">
 	  <div >
-	    <input type="text"/>
-	    <input id="find" type="button" value="Find Groups"/>
+	    <input id="associateFilter" type="text"/>
+	    <input id="find" type="button" value="Find"/>
 	    <span style="float:right">
-	      <input id="cancelEnrollGroups" type="button" value="Cancel"/>
-	      <input id="enrollGroups" type="button" value="Enroll"/>
+	      <input id="cancel" type="button" value="Cancel"/>
+	      <input id="enroll" type="button" value="Enroll"/>
 	    </span>
 	  </div>
 	  <div  id="results"  style="border: 2px solid rgb(0, 0, 0); position:relative;  height:200px;" >
 	    <div style="float:left;">
-	      <div>Groups</div>
-	      <select id="grouplist" width="150px" size="10" multiple="true" >
+	      <div>Available</div>
+	      <select id="availableList" width="150px" size="10" multiple="true" >
 	      </select>
 	    </div>
 	    <div style="float:left;">
@@ -114,7 +113,7 @@
 	      <p><input id="addToList" type="button" value="&gt;&gt;"/></p>
 	    </div>
 	    <div style="float:left;">
-	      <div>Prospective Enrollments</div>
+	      <div>Prospective</div>
 	      <select id="enrollments" width="150px"  size="10" multiple="true" >
 	      </select>
 	    </div>
diff --git a/install/static/ipa.js b/install/static/ipa.js
index 63101a7..d5a2dbd 100644
--- a/install/static/ipa.js
+++ b/install/static/ipa.js
@@ -38,10 +38,9 @@ var qs;
 var useSampleData = (window.location.protocol == "file:");
 
 
-function ipa_init(url, on_win)
+function ipa_init(on_win)
 {
-    if (!url)
-	url = '/ipa/json';
+    var url = '/ipa/json';
 
     _ipa_init_on_win_callback = on_win;
 
@@ -55,7 +54,10 @@ function ipa_init(url, on_win)
 
     $.ajaxSetup(options);
 
-    ipa_cmd('json_metadata', [], {}, _ipa_load_objs);
+    ipa_cmd('json_metadata', [], {}, _ipa_load_objs,
+            function(response){
+                alert('init failed');
+            });
 }
 
 function _ipa_load_objs(data, textStatus, xhr)
@@ -79,7 +81,9 @@ function ipa_cmd(name, args, options, win_callback, fail_callback, objname,sampl
     if (objname)
 	name = objname + '_' + name;
 
-    if (useSampleData && sampleData){
+    if (useSampleData){
+        var sampleData ="sampledata/"+name+".json";
+
 	var ajax_options = {
 	    url: sampleData,
 	    type: 'POST',
diff --git a/install/static/navigation.js b/install/static/navigation.js
index ff21fdf..94fe47a 100644
--- a/install/static/navigation.js
+++ b/install/static/navigation.js
@@ -119,14 +119,14 @@ function buildNavigation(){
         selectedSub.setup(facet);
     }
     }else if (currentMain && currentMain.setup){
-    currentMain.setup(facet);
+        currentMain.setup(facet);
     }
 
     var whoami = $.cookie("whoami");
     if (whoami == null){
-    ipa_cmd( 'whoami', [], {}, whoamiSuccess, null,null, "sampledata/whoami.json");
+        ipa_cmd( 'whoami', [], {}, whoamiSuccess, null,null);
     }else{
-    setLoggedInText(whoami);
+        setLoggedInText(whoami);
     }
 }
 
@@ -168,12 +168,41 @@ function setActiveTab(tabName){
     $(activeTab).removeClass('main-nav-off').addClass("main-nav-on")
 }
 
+
+function buildFacetNavigation(facets){
+        setupFacetNavigation(qs['tab'], qs['pkey'],qs['facet'], facets);
+}
+
+function setupFacetNavigation(tab, pkey,facet,facets){
+    $('#viewtype').css("display","block");
+    $("#viewtype").html("");
+
+    for (var i =0; i < facets.length; i++){
+        var li = $('<li>').appendTo($("#viewtype"));
+        if (facets[i] == facet){
+            $('<img src="but-selected.png" alt="" />');
+            li.html(facets[i]);
+        }else{
+            $('<img src="but-unselected.png" alt="" />').appendTo(li);
+            $('<a/>',{
+                href: "#tab="+tab+"&facet="+facets[i]+"&pkey="+pkey,
+                html: facets[i]
+            }).appendTo(li);
+        }
+    }
+}
+
 function clearOld(){
+
+    $('h1').html("");
+    $('#viewtype').css("display","none");
+
     $('#search').css("display","none");
     $('#details').css("display","none");
     $('#content').css("display","none");
     $('#associations').css("display","none");
 
+
     $('#searchResultsTable thead').html("");
     $('#searchResultsTable tfoot').html("");
     $('#searchResultsTable tbody').find("tr").remove();
@@ -194,11 +223,13 @@ function showSearch(){
 
 function showContent(){
     clearOld();
+    $('#viewtype').css("display","block");
     $('#content').css("display","block");
 }
 
 function showDetails(){
     clearOld();
+    $('#viewtype').css("display","block");
     $('#details').css("display","block");
 }
 
diff --git a/install/static/netgroup.js b/install/static/netgroup.js
index 5659fbd..88338b1 100644
--- a/install/static/netgroup.js
+++ b/install/static/netgroup.js
@@ -16,7 +16,7 @@ var netgroup_details_list =
         ['nisdomainname', 'NIS Domain']]]];
 
 
-var netgroupDetailsForm = new DetailsForm("netgroup",netgroup_details_list,"cn","sampledata/netgroupshow.json") ;
+var netgroupDetailsForm = new DetailsForm("netgroup",netgroup_details_list,"cn", ["details","hosts","groups","users"]) ;
 
 
 var netgroupAddProperties =
@@ -32,7 +32,6 @@ function netgroupAddOptionsFunction (){
     return options;
 }
 
-
 var netgroupBuilder = new EntityBuilder("netgroup",netgroupAddProperties,netgroupAddOptionsFunction);
 
 
@@ -43,4 +42,4 @@ var netgroupSearchColumns = [
     {title:"Description", column:"description",render: renderSimpleColumn}];
 
 var netgroupSearchForm =
-    new SearchForm("netgroup", "find", netgroupSearchColumns,"sampledata/netgrouplist.json");
+    new SearchForm("netgroup", "find", netgroupSearchColumns);
diff --git a/install/static/sampledata/group_add.json b/install/static/sampledata/group_add.json
new file mode 100644
index 0000000..180f5f1
--- /dev/null
+++ b/install/static/sampledata/group_add.json
@@ -0,0 +1,31 @@
+{
+    "error": null,
+    "id": 12,
+    "result": {
+        "result": {
+            "cn": [
+                "neighbors"
+            ],
+            "description": [
+                "The People in Your Neighborhood"
+            ],
+            "dn": "cn=neighbors,cn=groups,cn=accounts,dc=ayoung,dc=boston,dc=devel,dc=redhat,dc=com",
+            "gidnumber": [
+                "123456"
+            ],
+            "ipauniqueid": [
+                "0dbae87a-b43a-11df-ac8e-525400674dcd"
+            ],
+            "objectclass": [
+                "top",
+                "groupofnames",
+                "nestedgroup",
+                "ipausergroup",
+                "ipaobject",
+                "posixgroup"
+            ]
+        },
+        "summary": null,
+        "value": "neighbors"
+    }
+}
\ No newline at end of file
diff --git a/install/static/sampledata/group_add_member.json b/install/static/sampledata/group_add_member.json
new file mode 100644
index 0000000..7f10d10
--- /dev/null
+++ b/install/static/sampledata/group_add_member.json
@@ -0,0 +1,25 @@
+{
+    "error": null,
+    "id": 0,
+    "result": {
+        "completed": 1,
+        "failed": {
+            "member": {
+                "group": [],
+                "user": []
+            }
+        },
+        "result": {
+            "cn": [
+                "testgroup"
+            ],
+            "description": [
+                "testgroup"
+            ],
+            "dn": "cn=testgroup,cn=groups,cn=accounts,dc=ayoung,dc=boston,dc=devel,dc=redhat,dc=com",
+            "member_user": [
+                "kfrog"
+            ]
+        }
+    }
+}
\ No newline at end of file
diff --git a/install/static/sampledata/grouplist.json b/install/static/sampledata/group_find.json
similarity index 100%
rename from install/static/sampledata/grouplist.json
rename to install/static/sampledata/group_find.json
diff --git a/install/static/sampledata/groupshow.json b/install/static/sampledata/group_show.json
similarity index 100%
rename from install/static/sampledata/groupshow.json
rename to install/static/sampledata/group_show.json
diff --git a/install/static/sampledata/host_add.json b/install/static/sampledata/host_add.json
new file mode 100644
index 0000000..8b4d8b9
--- /dev/null
+++ b/install/static/sampledata/host_add.json
@@ -0,0 +1,40 @@
+{
+    "error": null,
+    "id": 14,
+    "result": {
+        "result": {
+            "cn": [
+                "www.pbs.org"
+            ],
+            "dn": "fqdn=www.pbs.org,cn=computers,cn=accounts,dc=ayoung,dc=boston,dc=devel,dc=redhat,dc=com",
+            "fqdn": [
+                "www.pbs.org"
+            ],
+            "has_keytab": false,
+            "ipauniqueid": [
+                "3ad2e63d-b43a-11df-8dd1-525400674dcd"
+            ],
+            "krbprincipalname": [
+                "host/www.pbs....@ayoung.boston.devel.redhat.com"
+            ],
+            "managedby": [
+                "fqdn=www.pbs.org,cn=computers,cn=accounts,dc=ayoung,dc=boston,dc=devel,dc=redhat,dc=com"
+            ],
+            "objectclass": [
+                "ipaobject",
+                "nshost",
+                "ipahost",
+                "pkiuser",
+                "ipaservice",
+                "krbprincipalaux",
+                "krbprincipal",
+                "top"
+            ],
+            "serverhostname": [
+                "www"
+            ]
+        },
+        "summary": null,
+        "value": "www.pbs.org"
+    }
+}
\ No newline at end of file
diff --git a/install/static/sampledata/hostlist.json b/install/static/sampledata/host_find.json
similarity index 100%
rename from install/static/sampledata/hostlist.json
rename to install/static/sampledata/host_find.json
diff --git a/install/static/sampledata/hostshow.json b/install/static/sampledata/host_show.json
similarity index 100%
rename from install/static/sampledata/hostshow.json
rename to install/static/sampledata/host_show.json
diff --git a/install/static/sampledata/hostgroup_add.json b/install/static/sampledata/hostgroup_add.json
new file mode 100644
index 0000000..bc41ee7
--- /dev/null
+++ b/install/static/sampledata/hostgroup_add.json
@@ -0,0 +1,27 @@
+{
+    "error": null,
+    "id": 15,
+    "result": {
+        "result": {
+            "cn": [
+                "guest_hosts"
+            ],
+            "description": [
+                "Special Guest Stars"
+            ],
+            "dn": "cn=guest_hosts,cn=hostgroups,cn=accounts,dc=ayoung,dc=boston,dc=devel,dc=redhat,dc=com",
+            "ipauniqueid": [
+                "7f8c57ae-b43a-11df-8016-525400674dcd"
+            ],
+            "objectclass": [
+                "ipaobject",
+                "ipahostgroup",
+                "nestedGroup",
+                "groupOfNames",
+                "top"
+            ]
+        },
+        "summary": "Added hostgroup \"guest_hosts\"",
+        "value": "guest_hosts"
+    }
+}
\ No newline at end of file
diff --git a/install/static/sampledata/hostgroup_add_member.json b/install/static/sampledata/hostgroup_add_member.json
new file mode 100644
index 0000000..a922017
--- /dev/null
+++ b/install/static/sampledata/hostgroup_add_member.json
@@ -0,0 +1,37 @@
+{
+    "error": null,
+    "id": 0,
+    "result": {
+        "completed": 3,
+        "failed": {
+            "member": {
+                "host": [],
+                "hostgroup": []
+            }
+        },
+        "result": {
+            "cn": [
+                "host-live"
+            ],
+            "description": [
+                "Live servers"
+            ],
+            "dn": "cn=host-live,cn=hostgroups,cn=accounts,dc=ayoung,dc=boston,dc=devel,dc=redhat,dc=com",
+            "ipauniqueid": [
+                "1ba89342-b683-11df-8d5b-525400674dcd"
+            ],
+            "member_host": [
+                "live3.pbs.org",
+                "live2.pbs.org",
+                "live1.pbs.org"
+            ],
+            "objectclass": [
+                "ipaobject",
+                "ipahostgroup",
+                "nestedGroup",
+                "groupOfNames",
+                "top"
+            ]
+        }
+    }
+}
\ No newline at end of file
diff --git a/install/static/sampledata/hostgrouplist.json b/install/static/sampledata/hostgroup_find.json
similarity index 100%
rename from install/static/sampledata/hostgrouplist.json
rename to install/static/sampledata/hostgroup_find.json
diff --git a/install/static/sampledata/hostgroupshow.json b/install/static/sampledata/hostgroup_show.json
similarity index 65%
rename from install/static/sampledata/hostgroupshow.json
rename to install/static/sampledata/hostgroup_show.json
index cddc41f..776d446 100644
--- a/install/static/sampledata/hostgroupshow.json
+++ b/install/static/sampledata/hostgroup_show.json
@@ -9,7 +9,12 @@
             "description": [
                 "Live servers"
             ],
-            "dn": "cn=host-live,cn=hostgroups,cn=accounts,dc=ayoung,dc=boston,dc=devel,dc=redhat,dc=com"
+            "dn": "cn=host-live,cn=hostgroups,cn=accounts,dc=ayoung,dc=boston,dc=devel,dc=redhat,dc=com",
+            "member_host": [
+                "live3.pbs.org",
+                "live2.pbs.org",
+                "live1.pbs.org"
+            ]
         },
         "summary": null,
         "value": "host-live"
diff --git a/install/static/sampledata/metadata.json b/install/static/sampledata/json_metadata.json
similarity index 100%
rename from install/static/sampledata/metadata.json
rename to install/static/sampledata/json_metadata.json
diff --git a/install/static/sampledata/netgroup_add.json b/install/static/sampledata/netgroup_add.json
new file mode 100644
index 0000000..989bb5d
--- /dev/null
+++ b/install/static/sampledata/netgroup_add.json
@@ -0,0 +1,28 @@
+{
+    "error": null,
+    "id": 17,
+    "result": {
+        "result": {
+            "cn": [
+                "butterfly_net"
+            ],
+            "description": [
+                "Butterfly Networks"
+            ],
+            "dn": "ipauniqueid=b16f7240-b43a-11df-89fa-525400674dcd,cn=ng,cn=alt,dc=ayoung,dc=boston,dc=devel,dc=redhat,dc=com",
+            "ipauniqueid": [
+                "b16f7240-b43a-11df-89fa-525400674dcd"
+            ],
+            "nisdomainname": [
+                "ayoung.boston.devel.redhat.com"
+            ],
+            "objectclass": [
+                "ipaobject",
+                "ipaassociation",
+                "ipanisnetgroup"
+            ]
+        },
+        "summary": null,
+        "value": "butterfly_net"
+    }
+}
\ No newline at end of file
diff --git a/install/static/sampledata/netgrouplist.json b/install/static/sampledata/netgroup_find.json
similarity index 100%
rename from install/static/sampledata/netgrouplist.json
rename to install/static/sampledata/netgroup_find.json
diff --git a/install/static/sampledata/netgroupshow.json b/install/static/sampledata/netgroup_show.json
similarity index 100%
rename from install/static/sampledata/netgroupshow.json
rename to install/static/sampledata/netgroup_show.json
diff --git a/install/static/sampledata/user_add.json b/install/static/sampledata/user_add.json
new file mode 100644
index 0000000..b2ede8d
--- /dev/null
+++ b/install/static/sampledata/user_add.json
@@ -0,0 +1,50 @@
+{
+    "error": null,
+    "id": 10,
+    "result": {
+        "result": {
+            "dn": "uid=snuffy,cn=users,cn=accounts,dc=ayoung,dc=boston,dc=devel,dc=redhat,dc=com",
+            "gecos": [
+                "snuffy"
+            ],
+            "givenname": [
+                "Aloysius"
+            ],
+            "homedirectory": [
+                "/home/snuffy"
+            ],
+            "ipauniqueid": [
+                "a4ce7d19-b439-11df-a9ae-525400674dcd"
+            ],
+            "krbprincipalname": [
+                "snu...@ayoung.boston.devel.redhat.com"
+            ],
+            "loginshell": [
+                "/bin/sh"
+            ],
+            "objectclass": [
+                "top",
+                "person",
+                "organizationalperson",
+                "inetorgperson",
+                "inetuser",
+                "posixaccount",
+                "krbprincipalaux",
+                "krbticketpolicyaux",
+                "radiusprofile",
+                "ipaobject"
+            ],
+            "sn": [
+                "Snuffleupagus"
+            ],
+            "uid": [
+                "snuffy"
+            ],
+            "uidnumber": [
+                "1869788865"
+            ]
+        },
+        "summary": "Added user \"snuffy\"",
+        "value": "snuffy"
+    }
+}
\ No newline at end of file
diff --git a/install/static/sampledata/userlist.json b/install/static/sampledata/user_find.json
similarity index 100%
rename from install/static/sampledata/userlist.json
rename to install/static/sampledata/user_find.json
diff --git a/install/static/sampledata/usershow.json b/install/static/sampledata/user_show.json
similarity index 100%
rename from install/static/sampledata/usershow.json
rename to install/static/sampledata/user_show.json
diff --git a/install/static/search.js b/install/static/search.js
index 95ac482..c1f99ba 100644
--- a/install/static/search.js
+++ b/install/static/search.js
@@ -1,6 +1,3 @@
-//useSampleData is defined in index.xhtml.  Work around for development
-var sampleData;
-
 
 //Columns is an array of items in the form
 // {title, column,  render}
@@ -25,13 +22,19 @@ function  renderUnknownColumn(current,cell){
 }
 
 
-function renderPkeyColumn(form,current,cell){
+function renderPkeyColumn2(obj,pkeyCol,current,cell){
     $("<a/>",{
-    href:"#tab="+form.obj+"&facet=details&pkey="+current[form.pkeyCol],
-    html:  "" + current[form.pkeyCol],
+    href:"#tab="+obj+"&facet=details&pkey="+current[pkeyCol],
+    html:  "" + current[pkeyCol],
     }).appendTo(cell);
 }
 
+function renderPkeyColumn(form,current,cell){
+    renderPkeyColumn2(form.obj, form.pkeyCol,current, cell);
+}
+
+
+
 
 function renderDetailColumn(current,cell,pkey,obj){
     $("<a/>",{
@@ -42,7 +45,7 @@ function renderDetailColumn(current,cell,pkey,obj){
 
 
 
-function SearchForm(obj, method, cols, searchSampleData){
+function SearchForm(obj, method, cols){
 
     this.buildColumnHeaders =  function (){
     var columnHeaders  = document.createElement("tr");
@@ -66,67 +69,60 @@ function SearchForm(obj, method, cols, searchSampleData){
     }
 
     this.searchSuccess = function (json){
-    if (json.result.truncated){
-        $("#searchResultsTable tfoot").html("More than "+sizelimit+" results returned.  First "+ sizelimit+" results shown." );
-    }else{
-        $("#searchResultsTable tfoot").html(json.result.summary);
-    }
-    $("#searchResultsTable tbody").find("tr").remove();
-    for (var index = 0; index !=  json.result.result.length; index++){
-    var current = json.result.result[index];
-        $('#searchResultsTable tbody:last').append(this.renderResultRow(current));
-    }
+        if (json.result.truncated){
+            $("#searchResultsTable tfoot").html("More than "+sizelimit+" results returned.  First "+ sizelimit+" results shown." );
+        }else{
+            $("#searchResultsTable tfoot").html(json.result.summary);
+        }
+        $("#searchResultsTable tbody").find("tr").remove();
+        for (var index = 0; index !=  json.result.result.length; index++){
+            var current = json.result.result[index];
+            $('#searchResultsTable tbody:last').append(this.renderResultRow(current));
+        }
     }
 
     this.searchWithFilter = function(queryFilter){
-    var form = this;
-
-    $('#searchResultsTable tbody').html("");
-    $('#searchResultsTable tbody').html("");
-    $('#searchResultsTable tfoot').html("");
-
-    ipa_cmd(this.method,
-        [queryFilter],
-        {"all":"true"},
-        function(json){
-            form.searchSuccess(json);
-        },
-        function(json){
-            alert("Search Failed");
-        },form.obj, form.searchSampleData);
-
+        var form = this;
+
+        $('#searchResultsTable tbody').html("");
+        $('#searchResultsTable tbody').html("");
+        $('#searchResultsTable tfoot').html("");
+
+        ipa_cmd(this.method,
+                [queryFilter],
+                {"all":"true"},
+                function(json){
+                    form.searchSuccess(json);
+                },
+                function(json){
+                    alert("Search Failed");
+                },
+                form.obj);
     }
 
     this.setup = function(){
-    showSearch();
-
-    $('#searchResultsTable thead').html("");
-    $('#searchResultsTable tbody').html("");
-    $('#searchResultsTable tfoot').html("");
-
-    $("#new").click(function(){
-        location.hash="tab="+obj+"&facet=add";
-    });
-
-    $("#query").click(executeSearch);
-
-    this.buildColumnHeaders();
-
-    var params = ipa_parse_qs();
-
-    qs = location.hash.substring(1);
-    //TODO fix this hack.  since parse returns an object, I don't know how to see if that object has a"critia" property if criteria is null.
-    if (qs.indexOf("criteria") > 0)
-    {
-        this.searchWithFilter(params["criteria"]);
-    }
+        showSearch();
+
+        $('#searchResultsTable thead').html("");
+        $('#searchResultsTable tbody').html("");
+        $('#searchResultsTable tfoot').html("");
+        $("#new").click(function(){
+            location.hash="tab="+obj+"&facet=add";
+        });
+        $("#query").click(executeSearch);
+        this.buildColumnHeaders();
+        var params = ipa_parse_qs();
+        qs = location.hash.substring(1);
+        //TODO fix this hack.  since parse returns an object, I don't know how to see if that object has a"critia" property if criteria is null.
+        if (qs.indexOf("criteria") > 0)
+        {
+            this.searchWithFilter(params["criteria"]);
+        }
     }
 
     this.obj = obj;
     this.method = method;
     this.columns = cols;
-    this.searchSampleData = searchSampleData;
-
     this.setup();
 }
 executeSearch = function(){
diff --git a/install/static/user.js b/install/static/user.js
index 71bf3e5..190afca 100644
--- a/install/static/user.js
+++ b/install/static/user.js
@@ -49,7 +49,7 @@ function setupUser(facet){
     }else  if (facet == "group"){
         setupUserGroupList();
     }else  if (facet == "groupmembership"){
-        setupUserGroupMembership();
+        userGroupMembershipForm.setup();
     }else{
         userSearchForm.setup();
     }
@@ -94,29 +94,13 @@ var userBuilder =
         });
 
 
-function setupFacetNavigation(pkey,facet){
-    $("#viewtype").html("");
-    var facets = ["details","group", "groupmembership"];
-
-    for (var i =0; i < facets.length; i++){
-        var li = $('<li>').appendTo($("#viewtype"));
-        if (facets[i] == facet){
-            $('<img src="but-selected.png" alt="" />');
-            li.html(facets[i]);
-        }else{
-            $('<img src="but-unselected.png" alt="" />').appendTo(li);
-            $('<a/>',{
-                href: "#tab=user&facet="+facets[i]+"&pkey="+pkey,
-                html: facets[i]
-            }).appendTo(li);
-        }
-    }
-}
+var userFacets = ["details","group", "groupmembership"];
+
 
 function setupUserDetails(user){
     qs = ipa_parse_qs();
-    setupFacetNavigation(qs.pkey,qs.facet);
     showDetails();
+    setupFacetNavigation('user',qs.pkey,qs.facet,userFacets);
     renderUserDetails();
 }
 
@@ -128,7 +112,7 @@ function renderUserDetails()
     if (qs['principal']) {
         ipa_cmd(
             'find', [], {'krbprincipalname': qs['principal']},
-            on_win_find, null, 'user', "sampledata/usershow.json");
+            on_win_find, null, 'user');
 
         return;
     }
@@ -136,7 +120,7 @@ function renderUserDetails()
     if (!qs['pkey'])
         return;
 
-    ipa_details_load(qs['pkey'], on_win, null, "sampledata/usershow.json");
+    ipa_details_load(qs['pkey'], on_win, null);
     $('h1').text('Managing user: ' + qs['pkey']);
 }
 
@@ -188,7 +172,7 @@ var userSearchColumns  = [
     {title:"Actions",  column:"none",           render: renderUserLinks}
 ];
 
-var userSearchForm = new SearchForm("user", "find", userSearchColumns,  "sampledata/userlist.json");
+var userSearchForm = new SearchForm("user", "find", userSearchColumns);
 
 /*Usr group enrollement:
   given a user, manage the groups in which they are enrolled */
@@ -196,146 +180,26 @@ function populateUserGroupFailure(){
     alert("Can't find user");
 }
 
-function setupUserGroupMembership(pkey){
-    sampleData = "sampledata/usershow.json";
-    showAssociations();
-    qs = ipa_parse_qs();
-    setupFacetNavigation(qs['pkey'],qs['facet']);
-
-    $('h1').text('Enroll user ' + qs['pkey'] + ' in groups');
-
-    $("#enrollGroups").click(function(){
-        groupsToEnroll =  [];
-        $('#enrollments').children().each(function(i, selected){
-            groupsToEnroll.push(selected.value);
-        });
-
-        currentUserToEnroll = qs['pkey'];
-        enrollUserInNextGroup();
-    });
-
-    $("#addToList").click(function(){
-        $('#grouplist :selected').each(function(i, selected){
-            $("#enrollments").append(selected);
-        });
-        $('#grouplist :selected').remove();
-    });
-
-    $("#removeFromList").click(function(){
-        $('#enrollments :selected').each(function(i, selected){
-            $("#grouplist").append(selected);
-        });
-        $('#enrollments :selected').remove();
-    });
-
-    $("#find").click(function(){
-        ipa_cmd( 'find', [], {}, populateUserGroupSearch, populateUserGroupFailure, 'group', "sampledata/grouplist.json" );
-
-    });
-}
-
-function populateUserGroupSearch(searchResults){
-    results = searchResults.result;
-    $("#grouplist").html("");
-    for (var i =0; i != searchResults.result.count; i++){
-        var li = document.createElement("option");
-        li.value = searchResults.result.result[i].cn;
-        li.innerHTML = searchResults.result.result[i].cn;
-        $("#grouplist").append(li);
-    }
-}
 
-var currentUserToEnroll;
-var groupsToEnroll;
 
-function enrollUserInGroupSuccess(response){
-    enrollUserInNextGroup();
-}
 
-function enrollUserInGroupFailure(response){
-    alert("enrollUserInGroupFailure");
-}
-
-function enrollUserInNextGroup(){
-    var  currentGroupToEnroll =     groupsToEnroll.shift();
-
-    if (currentGroupToEnroll){
-        var options = {"user":currentUserToEnroll};
-        var args = [currentGroupToEnroll];
-
-        ipa_cmd( 'add_member',args, options ,
-                 enrollUserInGroupSuccess,
-                 enrollUserInGroupFailure,'group' );
-    }else{
-        location.hash="tab=user&facet=group&pkey="+qs.pkey;
-    }
-}
-
-function renderUserGroupColumn(){
-}
-
-/*Group Membership&*/
-
-function  renderUserGroupColumn(current,cell){
-    cell.innerHTML = "Nothing to see here";
+var userGroupMembershipForm = new AssociationForm("user","group","groupmembership",userFacets, "cn", function(){
+    return 'Enroll user '  + qs['pkey'] + ' in groups';
 }
+);
 
+/*Group Membership*/
 var groupMembershipColumns  = [
-    {title:"Group",       column:"cn",        render: renderUserGroupColumn},
-    {title:"GID",         column:"gid",       render: renderUserGroupColumn},
-    {title:"Description", column:"uidnumber", render: renderUserGroupColumn},
-
+    {title:"Group", column:"memberof_group"},
 ];
 
-
 function populateUserEnrollments(userData){
-
-    var memberof_group = userData.result.result.memberof_group
-    for (var j = 0; j < memberof_group.length; j++){
-        var row  = document.createElement("tr");
-
-        var td = document.createElement("td");
-        td.innerHTML = memberof_group[j];
-        row.appendChild(td);
-
-        td = document.createElement("td");
-        td.innerHTML = "TBD";
-        row.appendChild(td);
-
-        var td = document.createElement("td");
-        td.innerHTML = "TBD";
-        row.appendChild(td);
-
-        $('#searchResultsTable thead:last').append(row);
-    }
+    userEnrollmentList.populate(userData);
 }
-
-
-function setupUserGroupList(){
-    qs = ipa_parse_qs();
-    setupFacetNavigation(qs['pkey'],qs['facet']);
-    showSearch();
-
-    $("#filter").css("display","none");
-
-    $("#searchButtons").html("");
-    $("<input/>",{
-        type:  'button',
-        value: 'enroll',
-        click: function(){
-            location.hash="tab=user&facet=groupmembership&pkey="+qs['pkey'];
-        }
-    }).appendTo("#searchButtons");
-    var columnHeaders  = document.createElement("tr");
-    for (var i =0 ; i != groupMembershipColumns.length ;i++){
-        var th = document.createElement("th");
-        th.innerHTML = groupMembershipColumns[i].title;
-        columnHeaders.appendChild(th);
-    }
-    $('#searchResultsTable thead:last').append(columnHeaders);
-
-    ipa_cmd( 'show', [qs['pkey']], {}, populateUserEnrollments, populateUserGroupFailure, 'user',"sampledata/usershow.json" );
+function  setupUserGroupList(){
+    userEnrollmentList.setup();
 }
+var userEnrollmentList = new AssociationList('user', 'group','groupmembership',groupMembershipColumns,userFacets);
 
 
 function on_win(data, textStatus, xhr)
-- 
1.7.1

_______________________________________________
Freeipa-devel mailing list
Freeipa-devel@redhat.com
https://www.redhat.com/mailman/listinfo/freeipa-devel

Reply via email to