diff --git a/application/controllers/ApprovalbodyadminController.php b/application/controllers/ApprovalbodyadminController.php
new file mode 100644
index 0000000000000000000000000000000000000000..655098e72f8cd4b4cbd9bba187479dd8e36ed8d4
--- /dev/null
+++ b/application/controllers/ApprovalbodyadminController.php
@@ -0,0 +1,101 @@
+<?php
+
+class ApprovalBodyAdminController extends Nmc_Controller_Action
+{
+    public function indexAction()
+    {
+        $out = new Nmc_View();
+        $out->page = 'approval_body_admin';
+
+        $out->approvalBodies = ApprovalBodies::getInstance()->fetchAll();
+
+        echo $out->render();
+    }
+
+    public function editBodyAction()
+    {
+        $in = $this->_getAllParams();
+        $approvalBodyId = Zend_Filter::getInt($in['URI_PARAMS'][0]);
+
+        $out = new Nmc_View();
+        $out->page = 'approval_body_admin';
+
+        $out->approvalBody = ApprovalBodies::getInstance()->find($approvalBodyId);
+        $out->approvalBodies = ApprovalBodies::getInstance()->fetchAll();
+
+        echo $out->render();
+    }
+
+    public function editRoleAction()
+    {
+        $in = $this->_getAllParams();
+        $roleId = Zend_Filter::getInt($in['URI_PARAMS'][0]);
+
+        $out = new Nmc_View();
+        $out->page = 'approval_body_admin';
+
+        $out->groups = Groups::getInstance()->fetchAll();
+        $out->approvalBodyRole = ApprovalBodyRoles::getInstance()->find($roleId);
+        $out->approvalBodies = ApprovalBodies::getInstance()->fetchAll();
+
+        echo $out->render();
+    }
+
+    public function addRoleAction()
+    {
+        $in = $this->_getAllParams();
+        $approvalBodyId = Zend_Filter::getInt($in['URI_PARAMS'][0]);
+        $parentApprovalBody = ApprovalBodies::getInstance()->find($approvalBodyId);
+
+        $out = new Nmc_View();
+        $out->page = 'approval_body_admin';
+
+        $out->groups = Groups::getInstance()->fetchAll();
+        $out->approvalBodyRole = ApprovalBodyRoles::getInstance()->fetchNew();
+        $out->approvalBodyRole->approvalBody = $parentApprovalBody->getPrimaryKey();
+        $out->approvalBodies = ApprovalBodies::getInstance()->fetchAll();
+
+        echo $out->render();
+    }
+
+    public function editBodyPostAction()
+    {
+        $in = $this->_getAllParams();
+        $bodyId = Zend_Filter::getInt($in['URI_PARAMS'][0]);
+        $body = ApprovalBodies::getInstance()->find($bodyId);
+
+        if(!$body) {
+            $body = ApprovalBodies::getInstance()->fetchNew();
+        }
+
+        $body->name = $in['name'];
+        $body->description = $in['description'];
+        $body->save();
+
+        $out = new Nmc_View();
+        $out->refresh = '/ApprovalBodyAdmin/EditBody/' . $body->getPrimaryKey();
+        echo $out->render();
+    }
+
+    public function editRolePostAction()
+    {
+        $in = $this->_getAllParams();
+        $roleId = Zend_Filter::getInt($in['URI_PARAMS'][0]);
+        if($roleId < 0) {
+            $role = ApprovalBodyRoles::getInstance()->fetchNew();
+            $role->approvalBody = abs($roleId);
+        } else {
+            $role = ApprovalBodyRoles::getInstance()->find($roleId);
+        }
+
+        $role->name = $in['name'];
+        $role->group = $in['group'];
+        $role->save();
+
+        $out = new Nmc_View();
+        $out->refresh = '/ApprovalBodyAdmin/EditRole/' . $role->getPrimaryKey();
+        echo $out->render();
+    }
+}
+
+?>
\ No newline at end of file
diff --git a/application/controllers/AuthController.php b/application/controllers/AuthController.php
index d5ac18c50b8b165d352bdd80feefa037113c66f1..ec852e955b664c04380bf456347e2ab16cfa2c19 100644
--- a/application/controllers/AuthController.php
+++ b/application/controllers/AuthController.php
@@ -23,6 +23,7 @@ class AuthController extends Nmc_Controller_Action
             $auth = new Nmc_Auth_Multi();
             $ldap = new Nmc_Ldap('ldap://localhost:10389');
             $authTable = new Auth();
+            //$auth->push(new Nmc_Auth_Always());
             $auth->push(new Nmc_Auth_Ldap($ldap));
             $auth->push(new Nmc_Auth_Mysql($authTable));
             $auth->login($in->getRaw('user_name'), $in->getRaw('password'));
@@ -34,11 +35,31 @@ class AuthController extends Nmc_Controller_Action
                 $filter = 'uid='
                         . strtr($in->getRaw('user_name'),
                                 array(',' => '', '=' => '', ' ' => ''));
-
+                $ldap->bind('uid=tsteiner2,ou=people,dc=unl,dc=edu', $in->getRaw('password'));
                 $userInfo = $ldap->search('ou=people,dc=unl,dc=edu', $filter);
+
+                $departmentIn = $userInfo[0]['unlhrprimarydepartment'][0];
+                $department = Departments::getInstance()->fetchByName($departmentIn);
+                if(!$department) {
+                    $departments = Departments::getInstance()->fetchAll();
+                    $minDiff = 99999;
+                    $bestMatch = null;
+                    foreach($departments as $row) {
+                        $diff = levenshtein($departmentIn, $row->name);
+                        if($diff < $minDiff) {
+                            $minDiff = $diff;
+                            $bestMatch = $row;
+                        }
+                    }
+                    $department = $bestMatch;
+                }
+
                 $user->userName = $in->getRaw('user_name');
                 $user->firstName = $userInfo[0]['givenname'][0];
                 $user->lastName = $userInfo[0]['sn'][0];
+                $user->email = $userInfo[0]['mail'][0];
+                $user->phone = $userInfo[0]['telephonenumber'][0];
+                $user->department = $department->getPrimaryKey();
                 $user->save();
             }
             Nmc_User::getInstance()->login($user);
@@ -60,4 +81,6 @@ class AuthController extends Nmc_Controller_Action
         echo $out->render();
     }
 
-}
\ No newline at end of file
+}
+
+?>
\ No newline at end of file
diff --git a/application/controllers/ConflictController.php b/application/controllers/ConflictController.php
index 65846a343b2b1d20972b1f09996c92846158ca4b..e0a7175d9d1516e4807784845d9e91a573703f79 100644
--- a/application/controllers/ConflictController.php
+++ b/application/controllers/ConflictController.php
@@ -19,12 +19,12 @@ FROM creq_course_crosslistings AS a,
      creq_course_generations AS c2,
      creq_course_codes AS d
 WHERE a.course_code = b.course_code
-  AND a.id != b.id
-  AND a.generation = c.id
+  AND a.course_crosslisting_id != b.course_crosslisting_id
+  AND a.generation = c.course_generation_id
   AND c.type = 'official'
-  AND b.generation = c2.id
+  AND b.generation = c2.course_generation_id
   AND c2.type = 'official'
-  AND a.course_code = d.id
+  AND a.course_code = d.course_code_id
 ORDER BY subject, course_number, course_letter
 EOF;
 
@@ -48,7 +48,7 @@ EOF;
 
         $allIds = array();
         foreach($dups as $dup) {
-            $allIds[] = $dup->id;
+            $allIds[] = $dup->getPrimaryKey();
         }
 
         $out = new Nmc_View();
diff --git a/application/controllers/CourseadminController.php b/application/controllers/CourseadminController.php
index 9988caed266d6d13273ff1208959304e29ccf32a..85026a3ce492509e05f3ce09bc2fb3fced34f156 100644
--- a/application/controllers/CourseadminController.php
+++ b/application/controllers/CourseadminController.php
@@ -36,7 +36,7 @@ class CourseAdminController extends Nmc_Controller_Action
             if($nextCourse = $crosslisting->getNextAlphabetically()) {
                 $nextCourse = $nextCourse->getParentCourse();
                 $nextCourseLink = '/' . $nextCourse->subject . '/' . $nextCourse->courseNumber . '/' . $nextCourse->courseLetter;
-                if($nextCourse->subject != $course->subject || $nextCourse->id == $course->id) {
+                if($nextCourse->subject != $course->subject || $nextCourse->getPrimaryKey() == $course->getPrimaryKey()) {
                     foreach($nextCourse->crosslistings as $crosslisting) {
                         if($crosslisting->subject == $course->subject) {
                             $nextCourseLink = '/' . $crosslisting->subject . '/' . $crosslisting->courseNumber . '/' . $crosslisting->courseLetter;
@@ -49,7 +49,7 @@ class CourseAdminController extends Nmc_Controller_Action
             if($prevCourse = $crosslisting->getPreviousAlphabetically()) {
                 $prevCourse = $prevCourse->getParentCourse();
                 $prevCourseLink = '/' . $prevCourse->subject . '/' . $prevCourse->courseNumber . '/' . $prevCourse->courseLetter;
-                if($prevCourse->subject != $course->subject || $prevCourse->id == $course->id) {
+                if($prevCourse->subject != $course->subject || $prevCourse->getPrimaryKey() == $course->getPrimaryKey()) {
                     foreach($prevCourse->crosslistings as $crosslisting) {
                         if($crosslisting->subject == $course->subject) {
                             $prevCourseLink = '/' . $crosslisting->subject . '/' . $crosslisting->courseNumber . '/' . $crosslisting->courseLetter;
@@ -169,7 +169,7 @@ class CourseAdminController extends Nmc_Controller_Action
         $course->save();
 
         $out = new Nmc_View();
-        $out->refresh = '/courseadmin/index/' . $course->id;
+        $out->refresh = '/courseadmin/index/' . $course->getPrimaryKey();
         echo $out->render();
     }
 
diff --git a/application/controllers/HomeController.php b/application/controllers/HomeController.php
index eafda27affb3e7b13561e3fba420cedcbf676372..1eb13d38cb0123fc4ed59278d7a85fd0149efcbd 100644
--- a/application/controllers/HomeController.php
+++ b/application/controllers/HomeController.php
@@ -8,13 +8,17 @@ class HomeController extends Nmc_Controller_Action
         $this->_registerPlugin(new Nmc_Controller_Action_Plugin_Authorize());
     }
 
-    public function IndexAction()
+    public function indexAction()
     {
         $user = Nmc_User::getInstance()->getUser();
         $requests = Requests::getInstance()->getRequestsForUser($user);
 
+        $roles = ApprovalBodyRoles::getInstance()->fetchRolesForUser($user);
+
+
         $out = new Nmc_View();
-        $out->requests = $requests;
+        $out->roles = $roles;
+        $out->myRequests = $requests;
         $out->page = 'my_home';
 
         echo $out->render();
diff --git a/application/controllers/TestController.php b/application/controllers/TestController.php
index 94716656312988afaab47a0e294dab6241f4dc3c..9bad73cea6d31fa7695b66e38cd850d21323ec1c 100644
--- a/application/controllers/TestController.php
+++ b/application/controllers/TestController.php
@@ -5,12 +5,19 @@ class TestController extends Nmc_Controller_Action
     public function __construct()
     {
         //$this->_registerPlugin(new Nmc_Controller_Action_Plugin_Authorize());
-        $this->_registerPlugin(new Nmc_Controller_Action_Plugin_Test());
+        //$this->_registerPlugin(new Nmc_Controller_Action_Plugin_Test());
     }
 
     public function indexAction()
     {
-        $foo = new Nmc_XMPP_Core();
+        header('Content-type: text/plain');
+        $test = Test::getInstance();
+        $rows = $test->fetchAll();
+        foreach($rows as $row) {
+            echo 'Creation time: ' . $row->creationTime . "\n";
+            echo 'Modification time: ' . $row->modifiedTime . "\n";
+            echo 'Asset ID: ' . $row->getAssetId() . "\n";
+        }
     }
 }
 
diff --git a/application/controllers/UseradminController.php b/application/controllers/UseradminController.php
new file mode 100644
index 0000000000000000000000000000000000000000..d52ff86f9c2890dd00fdad93f2d9ed130eab219a
--- /dev/null
+++ b/application/controllers/UseradminController.php
@@ -0,0 +1,160 @@
+<?php
+
+class UserAdminController extends Nmc_Controller_Action
+{
+    public function indexAction()
+    {
+        $out = new Nmc_View();
+
+        $groups = Groups::getInstance()->fetchAllWithoutPrimaries();
+
+        $out->title = 'Group Administration';
+        $out->page = 'user_admin';
+        $out->groups = $groups;
+        echo $out->render();
+    }
+
+    public function editUserAction()
+    {
+        $in = $this->_getAllParams();
+        $userId = Zend_Filter::getInt($in['URI_PARAMS'][0]);
+
+        if($in['Submit'] == 'Submit') {
+            return $this->editUserActionPost();
+        }
+
+        $out = new Nmc_View();
+
+        $out->title = 'Group Administration';
+        $out->page = 'user_admin';
+
+        if($userId < 0) {
+            $out->user = People::getInstance()->fetchNew();
+        } else {
+            $out->user = People::getInstance()->findByUserId($userId);
+        }
+        $out->groups = Groups::getInstance()->fetchAllWithoutPrimaries();
+
+        echo $out->render();
+    }
+
+    protected function editUserActionPost()
+    {
+        $in = $this->_getAllParams();
+        $userId = Zend_Filter::getInt($in['URI_PARAMS'][0]);
+        if(!$userId) {
+            $user = People::getInstance()->fetchNew();
+        } else {
+            $user = People::getInstance()->find($userId);
+        }
+
+        if($in['delete'] == '1') {
+            $user->delete();
+        } else {
+            $user->firstName = Zend_Filter::getAlnum($in['firstName']);
+            $user->lastName = Zend_Filter::getAlnum($in['lastName']);
+            $user->save();
+        }
+
+        $selectedGroups = Groups::getInstance()->findAll($in['groups']);
+        $currentGroups = $user->getGroups(false);
+
+        $removedGroups = $currentGroups->getRowsNotInCommonWith($selectedGroups);
+        $newGroups = $selectedGroups->getRowsNotInCommonWith($currentGroups);
+
+        foreach($removedGroups as $removedGroup) {
+            $removedGroup->removeUser($user);
+        }
+
+        foreach($newGroups as $newGroup)
+        {
+            $newGroup->addUser($user);
+        }
+
+
+        $out = new Nmc_View();
+        $out->refresh = '/UserAdmin/EditUser/' . $userId;
+        echo $out->render();
+    }
+
+    public function editGroupAction()
+    {
+        $in = $this->_getAllParams();
+        $groupId = Zend_Filter::getInt($in['URI_PARAMS'][0]);
+
+        if($in['Submit'] == 'Submit') {
+            return $this->editGroupActionPost();
+        }
+
+        $out = new Nmc_View();
+
+        $out->title = 'Group Administration';
+        $out->page = 'user_admin';
+
+        if($groupId < 0) {
+            $out->group = Groups::getInstance()->fetchNew();
+        } else {
+            $out->group = Groups::getInstance()->find($groupId);
+        }
+        $out->groups = Groups::getInstance()->fetchAllWithoutPrimaries();
+        $out->users = People::getInstance()->fetchAll();
+        $out->groups = Groups::getInstance()->fetchAllWithoutPrimaries();
+
+        echo $out->render();
+    }
+
+    protected function editGroupActionPost()
+    {
+        $in = $this->_getAllParams();
+        $groupId = Zend_Filter::getInt($in['URI_PARAMS'][0]);
+        if(!$groupId) {
+            $group = Groups::getInstance()->fetchNew();
+            $group->type = 1;
+        } else {
+            $group = Groups::getInstance()->find($groupId);
+        }
+
+        if($in['delete'] == '1') {
+            $group->delete();
+        } else {
+            $group->name = $in['name'];
+            $group->description = $in['description'];
+            $group->save();
+        }
+
+        $selectedGroups = Groups::getInstance()->findAll($in['groups']);
+        $currentGroups = $group->getGroups(false);
+
+        $removedGroups = $currentGroups->getRowsNotInCommonWith($selectedGroups);
+        $newGroups = $selectedGroups->getRowsNotInCommonWith($currentGroups);
+
+        foreach($removedGroups as $removedGroup) {
+            $group->removeGroup($removedGroup);
+        }
+
+        foreach($newGroups as $newGroup)
+        {
+            $group->addGroup($newGroup);
+        }
+
+        $selectedUsers = People::getInstance()->findByUserId($in['users']);
+        $currentUsers = $group->getUsers(false);
+
+        $removedUsers = $currentUsers->getRowsNotInCommonWith($selectedUsers);
+        $newUsers = $selectedUsers->getRowsNotInCommonWith($currentUsers);
+
+        foreach($removedUsers as $removedUser) {
+            $group->removeUser($removedUser);
+        }
+
+        foreach($newUsers as $newUser) {
+            $group->addUser($newUser);
+        }
+
+        $out = new Nmc_View();
+        $out->refresh = '/UserAdmin/EditGroup/' . $groupId;
+        echo $out->render();
+    }
+}
+
+?>
\ No newline at end of file
diff --git a/application/library/Local/Db/CourseTableMany.php b/application/library/Local/Db/CourseTableMany.php
index 9cc737b9a4f9d91f33edb1e0479d525f1cee3e77..7a9fb893ed5cad66d8799f565698616f29cd1c2d 100644
--- a/application/library/Local/Db/CourseTableMany.php
+++ b/application/library/Local/Db/CourseTableMany.php
@@ -5,7 +5,7 @@ abstract class Local_Db_CourseTableMany extends Nmc_Db_Table
     public function fetchByGeneration(CourseGeneration $generation)
     {
         $where = array();
-        $where[] = $this->_db->quoteInto('generation = ?', $generation->id);
+        $where[] = $this->_db->quoteInto('generation = ?', $generation->getPrimaryKey());
         $where = implode(' AND ', $where);
 
         return $this->fetchAll($where);
diff --git a/application/library/Local/Db/CourseTableOne.php b/application/library/Local/Db/CourseTableOne.php
index 1d74f9c933a50fbf52276414637e7dcd98ab16f0..08ddad075d6374de05d10fb26f054b4447175314 100644
--- a/application/library/Local/Db/CourseTableOne.php
+++ b/application/library/Local/Db/CourseTableOne.php
@@ -5,7 +5,7 @@ abstract class Local_Db_CourseTableOne extends Nmc_Db_Table
     public function fetchByGeneration(CourseGeneration $generation)
     {
         $where = array();
-        $where[] = $this->_db->quoteInto('generation = ?', $generation->id);
+        $where[] = $this->_db->quoteInto('generation = ?', $generation->getPrimaryKey());
         $where = implode(' AND ', $where);
 
         return $this->fetchRow($where);
diff --git a/application/models/rows/ApprovalBody.php b/application/models/rows/ApprovalBody.php
new file mode 100644
index 0000000000000000000000000000000000000000..51e36af406e896ddd00aaa2ff61a8eda7cd48bd2
--- /dev/null
+++ b/application/models/rows/ApprovalBody.php
@@ -0,0 +1,20 @@
+<?php
+
+class ApprovalBody extends Nmc_Db_Table_Row
+{
+    function __construct($config = array())
+    {
+        parent::__construct($config);
+
+        $this->_registerRelation(
+            new Nmc_Db_Table_Relation_HasMany(
+                ApprovalBodyRoles::getInstance(),
+                $this,
+                'approval_body',
+                'roles'
+            )
+        );
+    }
+}
+
+?>
\ No newline at end of file
diff --git a/application/models/rows/ApprovalBodyRole.php b/application/models/rows/ApprovalBodyRole.php
new file mode 100644
index 0000000000000000000000000000000000000000..e2afea9a97c597825b1d7ca089a59e38bde54d71
--- /dev/null
+++ b/application/models/rows/ApprovalBodyRole.php
@@ -0,0 +1,33 @@
+<?php
+
+class ApprovalBodyRole extends Nmc_Db_Table_Row
+{
+    public function __construct($config = array())
+    {
+        parent::__construct($config);
+        $this->_registerRelation(
+            new Nmc_Db_Table_Relation_Extend(
+                Groups::getInstance(),
+                $this,
+                'group',
+                'roleGroup',
+                false
+            )
+        );
+    }
+
+    /**
+     * Return a rowset of requests pending for this role
+     *
+     * @return Nmc_Db_Table_Rowset
+     */
+    public function getRequests()
+    {
+        // If this is the "root" role
+        if($this->getPrimaryKey() == 1) {
+            return Requests::getInstance()->fetchAll();
+        }
+    }
+}
+
+?>
\ No newline at end of file
diff --git a/application/models/rows/Asset.php b/application/models/rows/Asset.php
index a72b4c9f12d2988a9c2c1d81ba38928881fb29b5..ea3aabdfba768b5881ef1fdd495bafa9a2af9983 100644
--- a/application/models/rows/Asset.php
+++ b/application/models/rows/Asset.php
@@ -1,10 +1,16 @@
 <?php
 
+/**
+ * Row class for the asset table
+ *
+ * @tableClass Assets
+ *
+ */
 class Asset extends Nmc_Db_Table_Row
 {
     static public $tableClass = 'Assets';
 
-    public function _save()
+    protected function _save()
     {
         if(!$this->creationTime) {
             $this->creationTime = time();
@@ -14,6 +20,11 @@ class Asset extends Nmc_Db_Table_Row
 
         parent::_save();
     }
+
+    public function getAssetId()
+    {
+        return $this->getPrimaryKey();
+    }
 }
 
 ?>
\ No newline at end of file
diff --git a/application/models/rows/CourseCode.php b/application/models/rows/CourseCode.php
index b3b85d9e072e2df3f0d5831bc6c2abb48309430a..08ecfeee1a42bc505fd67290c2d4ec15f0471a8e 100644
--- a/application/models/rows/CourseCode.php
+++ b/application/models/rows/CourseCode.php
@@ -20,7 +20,7 @@ class CourseCode extends Nmc_Db_Table_Row
         $where = implode(' AND ', $where);
 
         $row = $this->_table->fetchRow($where, $order);
-        if($row->id) {
+        if($row->getPrimaryKey()) {
             return $row;
         }
 
@@ -30,7 +30,7 @@ class CourseCode extends Nmc_Db_Table_Row
         $where = implode(' AND ', $where);
 
         $row = $this->_table->fetchRow($where, $order);
-        if($row->id) {
+        if($row->getPrimaryKey()) {
             return $row;
         }
 
@@ -39,7 +39,7 @@ class CourseCode extends Nmc_Db_Table_Row
         $where = implode(' AND ', $where);
 
         $row = $this->_table->fetchRow($where, $order);
-        if($row->id) {
+        if($row->getPrimaryKey()) {
             return $row;
         }
 
@@ -65,7 +65,7 @@ class CourseCode extends Nmc_Db_Table_Row
         $where = implode(' AND ', $where);
 
         $row = $this->_table->fetchRow($where, $order);
-        if($row->id) {
+        if($row instanceof Nmc_Db_Table_Row && $row->getPrimaryKey()) {
             return $row;
         }
 
@@ -75,7 +75,7 @@ class CourseCode extends Nmc_Db_Table_Row
         $where = implode(' AND ', $where);
 
         $row = $this->_table->fetchRow($where, $order);
-        if($row->id) {
+        if($row->getPrimaryKey()) {
             return $row;
         }
 
@@ -84,7 +84,7 @@ class CourseCode extends Nmc_Db_Table_Row
         $where = implode(' AND ', $where);
 
         $row = $this->_table->fetchRow($where, $order);
-        if($row->id) {
+        if($row->getPrimaryKey()) {
             return $row;
         }
 
diff --git a/application/models/rows/CourseCrosslisting.php b/application/models/rows/CourseCrosslisting.php
index 8f3f848cf92ef6d1d00b46ed7ed9115983cc1b5b..d6cad6d027d94bd82908382ef2b2c65e341e94a5 100644
--- a/application/models/rows/CourseCrosslisting.php
+++ b/application/models/rows/CourseCrosslisting.php
@@ -11,8 +11,9 @@ class CourseCrosslisting extends Nmc_Db_Table_Row
     public function __construct($config = array())
     {
         parent::__construct($config);
+        $primaryKeyName = CourseCodes::getInstance()->getPrimaryKeyName(true);
         $courseCode = CourseCodes::getInstance()->find($this->courseCode);
-        if($courseCode->id) {
+        if($courseCode->$primaryKeyName) {
             $this->_subject = $courseCode->subject;
             $this->_courseNumber = $courseCode->courseNumber;
             $this->_courseLetter = $courseCode->courseLetter;
@@ -58,7 +59,7 @@ class CourseCrosslisting extends Nmc_Db_Table_Row
         );
         $courseCode->integratedStudies = $this->_integratedStudies;
         $courseCode->save();
-        $this->courseCode = $courseCode->id;
+        $this->courseCode = $courseCode->getPrimaryKey();
         parent::_save();
     }
 
diff --git a/application/models/rows/CourseGeneration.php b/application/models/rows/CourseGeneration.php
index 1f2361e2e2834fce7a0afe3e70347f01fe1fee55..117acb18dc40ce89c68bac4bb0e7cbb021fc6d03 100644
--- a/application/models/rows/CourseGeneration.php
+++ b/application/models/rows/CourseGeneration.php
@@ -1,6 +1,12 @@
 <?php
 
-class CourseGeneration extends Nmc_Db_Table_Row
+/**
+ *
+ * @tableClass CourseGenerations
+ * @foreignKey assetId
+ *
+ */
+class CourseGeneration extends Asset
 {
     protected $_homeCrosslisting;
 
@@ -8,8 +14,6 @@ class CourseGeneration extends Nmc_Db_Table_Row
     {
         parent::__construct($config);
 
-        $this->_registerRelation(
-            new Nmc_Db_Table_Relation_Extend(Assets::getInstance(), $this, 'assetId'));
         $this->_registerRelation(
             new Nmc_Db_Table_Relation_HasOne(CourseDetails::getInstance(), $this, 'generation'));
         $this->_registerRelation(
@@ -30,7 +34,7 @@ class CourseGeneration extends Nmc_Db_Table_Row
 
     public function _clone()
     {
-        $parentId = $this->id;
+        $parentId = $this->getPrimaryKey();
 
         // duplicate parent record
         parent::_clone();
@@ -96,7 +100,7 @@ class CourseGeneration extends Nmc_Db_Table_Row
             if(!$this->course) {
                 $course = Courses::getInstance()->fetchNew();
                 $course->save();
-                $this->course = $course->id;
+                $this->course = $course->getPrimaryKey();
             }
             if(!$this->creationTime) {
                 $this->creationTime = time();
@@ -104,7 +108,7 @@ class CourseGeneration extends Nmc_Db_Table_Row
 
             if($this->request instanceof Request) {
                 $this->request->save();
-                $this->_data['request'] = $this->request->id;
+                $this->_data['request'] = $this->request->getPrimaryKey();
             }
 
             $this->_setHomeCrosslisting($this->_homeCrosslisting);
@@ -129,6 +133,11 @@ class CourseGeneration extends Nmc_Db_Table_Row
         }
     }
 
+    public function getAssetId()
+    {
+        return $this->assetId;
+    }
+
     /**
      * Returns a reference to the home crosslist
      *
diff --git a/application/models/rows/Group.php b/application/models/rows/Group.php
new file mode 100644
index 0000000000000000000000000000000000000000..6bd0f865a4444b0d54c68f67d120233aac64966a
--- /dev/null
+++ b/application/models/rows/Group.php
@@ -0,0 +1,83 @@
+<?php
+
+class Group extends Nmc_Db_Table_Row
+{
+    public function _get($name)
+    {
+        switch($name) {
+            case 'users':
+                return $this->getUsers();
+                break;
+            case 'groups':
+                return $this->getGroups();
+                break;
+
+            default:
+                return parent::_get($name);
+                break;
+        }
+    }
+
+    public function addUser($user)
+    {
+        $childGroup = Groups::getInstance()->find($user->primaryGroup);
+        $this->addGroup($childGroup);
+    }
+
+    public function removeUser($user)
+    {
+        $childGroup = Groups::getInstance()->find($user->primaryGroup);
+        $this->removeGroup($childGroup);
+    }
+
+    public function addGroup(Group $group)
+    {
+        $membership = GroupMemberships::getInstance()->fetchNew();
+        $membership->parentGroup = $this->getPrimaryKey();
+        $membership->childGroup = $group->getPrimaryKey();
+        $membership->type = $this->type;
+        $membership->save();
+    }
+
+    public function removeGroup(Group $group)
+    {
+        $where = array();
+        $where[] = $this->_db->quoteInto('parent_group = ?', $this->getPrimaryKey());
+        $where[] = $this->_db->quoteInto('child_group = ?', $group->getPrimaryKey());
+        $where = implode(' AND ', $where);
+        $membership = GroupMemberships::getInstance()->fetchRow($where);
+        $membership->delete();
+    }
+
+    /**
+     * Returns all users belonging to this group
+     *
+     * @param bool[optional] $impliedMemberships
+     * @param bool[optional] $explicitMemberships
+     * @return Nmc_Db_Table_Rowset
+     */
+    public function getUsers($impliedMemberships = true, $explicitMemberships = true)
+    {
+        $membershipTable = GroupImpliedMemberships::getInstance();
+        return $membershipTable->fetchUsersByParentGroup($this, $impliedMemberships, $explicitMemberships);
+    }
+
+    /**
+     * Returns all groups belonging to this group
+     *
+     * @param bool[optional] $impliedMemberships
+     * @param bool[optional] $explicitMemberships
+     * @return Nmc_Db_Table_Rowset
+     */
+    public function getGroups($impliedMemberships = true, $explicitMemberships = true)
+    {
+        $membershipTable = GroupImpliedMemberships::getInstance();
+        return $membershipTable->fetchGroupsByParentGroup($this, $impliedMemberships, $explicitMemberships);
+    }
+
+    public function isRootGroup()
+    {
+        $parentGroups = GroupImpliedMemberships::getInstance()->fetchGroupsByChildGroup($this);
+        return($parentGroups->count() === 0);
+    }
+}
\ No newline at end of file
diff --git a/application/models/rows/GroupImpliedMembership.php b/application/models/rows/GroupImpliedMembership.php
new file mode 100644
index 0000000000000000000000000000000000000000..fa6d7caa92ce98802846ace40b127a6477aa16ae
--- /dev/null
+++ b/application/models/rows/GroupImpliedMembership.php
@@ -0,0 +1,14 @@
+<?php
+
+class GroupImpliedMembership extends Nmc_Db_Table_Row
+{
+    public function _save()
+    {
+        throw new Zend_Db_Exception('Cannot manually edit implied memberships!');
+    }
+
+    public function delete()
+    {
+        throw new Zend_Db_Exception('Cannot manually edit implied memberships!');
+    }
+}
\ No newline at end of file
diff --git a/application/models/rows/Person.php b/application/models/rows/Person.php
index 10c51b85885db112a62c81b9203e078f6e65b031..de13ad8658106fa377750ba0985353fcb0e544c1 100644
--- a/application/models/rows/Person.php
+++ b/application/models/rows/Person.php
@@ -2,12 +2,37 @@
 
 Zend::loadInterface('Nmc_Model_UserInterface');
 
-class Person extends Nmc_Db_Table_Row implements Nmc_Model_UserInterface
+/**
+ *
+ * @foreignKey userId
+ * @tableClass People
+ *
+ */
+class Person extends User
+             implements Nmc_Model_UserInterface
 {
 
+    public function __construct($config = array())
+    {
+        parent::__construct($config);
+    }
+
+    public function _save()
+    {
+        if(!$this->primaryGroup) {
+            $primaryGroup = Groups::getInstance()->fetchNew();
+            $primaryGroup->type = 1;
+            $primaryGroup->name = $this->userName;
+            $primaryGroup->save();
+            $this->primaryGroup = $primaryGroup->getPrimaryKey();
+        }
+
+        return parent::_save();
+    }
+
     public function getId()
     {
-        return $this->id;
+        return $this->getPrimaryKey();
     }
 
     public function getUserName()
@@ -24,4 +49,21 @@ class Person extends Nmc_Db_Table_Row implements Nmc_Model_UserInterface
     {
         return $this->firstName . ' ' . $this->lastName;
     }
+
+    public function _get($name)
+    {
+        switch($name) {
+            case 'groups':
+                return GroupImpliedMemberships::getInstance()->fetchGroupsByUser($this);
+                break;
+            default:
+                return parent::_get($name);
+                break;
+        }
+    }
+
+    public function getGroups($implied = true, $explicit = true)
+    {
+        return GroupImpliedMemberships::getInstance()->fetchGroupsByUser($this, $implied, $explicit);
+    }
 }
\ No newline at end of file
diff --git a/application/models/rows/Request.php b/application/models/rows/Request.php
index 91f16e463a168630f237ca7ecc8639dffc6992eb..5df0ce0bbd424419184d6a7d0eed1fc166062949 100644
--- a/application/models/rows/Request.php
+++ b/application/models/rows/Request.php
@@ -18,6 +18,8 @@ class Request extends Nmc_Db_Table_Row
     public function getCourseGeneration()
     {
         $returnValue = null;
+        $primaryKey = $this->_table->getPrimaryKeyName();
+        $primaryKey = Nmc_Db_Inflector::getInstance()->camelize($primaryKey);
 
         $select = $this->_db->select();
         $select->from(CourseGenerations::getInstance()->getTableName(),
@@ -27,7 +29,7 @@ class Request extends Nmc_Db_Table_Row
                       . '.asset_id = '
                       . Assets::getInstance()->getTableName() . '.'
                       . Assets::getInstance()->getPrimaryKeyName());
-        $select->where($this->_db->quoteInto('request = ?', $this->id));
+        $select->where($this->_db->quoteInto('request = ?', $this->$primaryKey));
         $select->order('creation_time');
         $generations = $this->_db->fetchCol($select);
         if(count($generations) > 0) {
diff --git a/application/models/tables/ActivityTypes.php b/application/models/tables/ActivityTypes.php
index 6b4e35cfffb4710283d09d2f57504a5ea647a69e..2f85f3ce16b21c84ca0a4187e338d770fd50fc5d 100644
--- a/application/models/tables/ActivityTypes.php
+++ b/application/models/tables/ActivityTypes.php
@@ -1,5 +1,10 @@
 <?php
 
+/**
+ *
+ * @primary activity_type_id
+ *
+ */
 class ActivityTypes extends Nmc_Db_Table
 {
     /**
diff --git a/application/models/tables/ApprovalBodies.php b/application/models/tables/ApprovalBodies.php
index 897e92048a6677f985c4ea5e33ca90690a065fab..40410d5acce36a562cf900a2c970bceae85d06ba 100644
--- a/application/models/tables/ApprovalBodies.php
+++ b/application/models/tables/ApprovalBodies.php
@@ -1,5 +1,11 @@
 <?php
 
+
+/**
+ *
+ * @primary approval_body_id
+ *
+ */
 class ApprovalBodies extends Nmc_Db_Table
 {
     protected $_rowClass = 'ApprovalBody';
diff --git a/application/models/tables/ApprovalBodyRoles.php b/application/models/tables/ApprovalBodyRoles.php
new file mode 100644
index 0000000000000000000000000000000000000000..468e76391016ab7535c9d5150a8083a5f6515caf
--- /dev/null
+++ b/application/models/tables/ApprovalBodyRoles.php
@@ -0,0 +1,47 @@
+<?php
+
+/**
+ *
+ * @rowClass ApprovalBodyRole
+ * @primary approval_body_role_id
+ *
+ */
+class ApprovalBodyRoles extends Nmc_Db_Table
+{
+
+    /**
+     * The one true instance
+     *
+     * @var ApprovalBodyRoles
+     */
+    protected static $_instance = null;
+
+    /**
+     * Returns the one true instance
+     *
+     * @return ApprovalBodyRoles
+     */
+    public static function getInstance($config = array())
+    {
+        if(!self::$_instance) {
+            self::$_instance = new ApprovalBodyRoles($config);
+        }
+        return self::$_instance;
+    }
+
+    public function fetchRolesForUser(User $user)
+    {
+        $groups = GroupImpliedMemberships::getInstance()->fetchGroupsByUser($user);
+
+        $groupIds = array();
+        foreach($groups as $group) {
+            $groupIds[] = $group->getPrimaryKey();
+        }
+
+        $db = $this->_db;
+        $where = $db->quoteInto('`group` IN(?)', $groupIds);
+        return $this->fetchAll($where);
+    }
+}
+
+?>
\ No newline at end of file
diff --git a/application/models/tables/ApprovalChains.php b/application/models/tables/ApprovalChains.php
index 974cc347f9b90754130ebf3acb41213d81d95d91..92dfee503c13147379278c85f1f756599298902c 100644
--- a/application/models/tables/ApprovalChains.php
+++ b/application/models/tables/ApprovalChains.php
@@ -1,5 +1,10 @@
 <?php
 
+/**
+ *
+ * @primary approval_chain_id
+ *
+ */
 class ApprovalChains extends Nmc_Db_Table {}
 
 ?>
\ No newline at end of file
diff --git a/application/models/tables/ApprovalLinks.php b/application/models/tables/ApprovalLinks.php
index 1ef35557fda036978d342485e26f0bc07a9ea078..d58c522a89626aa94418e1053aa4ca351253051b 100644
--- a/application/models/tables/ApprovalLinks.php
+++ b/application/models/tables/ApprovalLinks.php
@@ -1,5 +1,10 @@
 <?php
 
+/**
+ *
+ * @primary approval_link_id
+ *
+ */
 class ApprovalLinks extends Nmc_Db_Table {}
 
 ?>
\ No newline at end of file
diff --git a/application/models/tables/Assets.php b/application/models/tables/Assets.php
index 5fe66a7f93add33c7d6583a4e0d20cd2f71d6c68..71a0198984ddf641837456df01530b4abf8c0ab1 100644
--- a/application/models/tables/Assets.php
+++ b/application/models/tables/Assets.php
@@ -1,5 +1,10 @@
 <?php
 
+/**
+ *
+ * @primary asset_id
+ *
+ */
 class Assets extends Nmc_Db_Table
 {
     protected $_rowClass = 'Asset';
diff --git a/application/models/tables/Auth.php b/application/models/tables/Auth.php
index c5489d3e7d13133b3add6417da87c27074c6aeb8..51ca2027f2546c1adb4aafe0597b926c72d2b54a 100644
--- a/application/models/tables/Auth.php
+++ b/application/models/tables/Auth.php
@@ -1,5 +1,12 @@
 <?php
 
+
+/**
+ *
+ * @table auth
+ * @primary auth_id
+ *
+ */
 class Auth extends Zend_Db_Table
 {
 
diff --git a/application/models/tables/CourseActivities.php b/application/models/tables/CourseActivities.php
index 97097ddd0835f11dc4ce8af293f8579935ba0f5a..a716e035ba6d2e3091af84a0575afc2a4eb3fbb9 100644
--- a/application/models/tables/CourseActivities.php
+++ b/application/models/tables/CourseActivities.php
@@ -1,5 +1,11 @@
 <?php
 
+
+/**
+ *
+ * @primary course_activity_id
+ *
+ */
 class CourseActivities extends Local_Db_CourseTableMany
 {
     protected static $_instance;
@@ -24,7 +30,7 @@ class CourseActivities extends Local_Db_CourseTableMany
         $me = self::getInstance();
         $config = array('db'    => $me->_db,
                         'table' => $me,
-                        'data'  => array('course_generation' => $course->id));
+                        'data'  => array('course_generation' => $course->getPrimaryKey()));
         $newRecord = new CourseActivity($config);
         return $newRecord;
     }
@@ -45,7 +51,7 @@ class CourseActivities extends Local_Db_CourseTableMany
                           . $activityTableName . '.short_name');
 
             $select->where($where);
-            $select->order($activityTableName . '.id');
+            $select->order($activityTableName . '.' . $activityPrimary);
             $select->limit($count, $offset);
             $resultData = $this->_db->fetchAll($select);
             $config = array();
diff --git a/application/models/tables/CourseCodes.php b/application/models/tables/CourseCodes.php
index a8821ab09adb4658676458aea8d92cf2cdbc954c..b9b98ae795becbfaa72104967f963dd4eae6efb5 100644
--- a/application/models/tables/CourseCodes.php
+++ b/application/models/tables/CourseCodes.php
@@ -1,5 +1,10 @@
 <?php
 
+/**
+ *
+ * @primary course_code_id
+ *
+ */
 class CourseCodes extends Nmc_Db_Table
 {
 
@@ -42,7 +47,8 @@ class CourseCodes extends Nmc_Db_Table
         $where = implode(' AND ', $where);
 
         $result = $this->fetchRow($where);
-        if(! $result->id) {
+        $primaryKeyName = $this->getPrimaryKeyName(true);
+        if(! $result->$primaryKeyName) {
             $result = $this->fetchNew();
             $result->subject = $subject;
             $result->courseNumber = $number;
diff --git a/application/models/tables/CourseCredits.php b/application/models/tables/CourseCredits.php
index b04f554c519cdb8b011e0b729c3bea4d11c13aa0..e23098661c7d5f058cde8655e17a7da21fb16e51 100644
--- a/application/models/tables/CourseCredits.php
+++ b/application/models/tables/CourseCredits.php
@@ -1,5 +1,10 @@
 <?php
 
+/**
+ *
+ * @primary course_credit_id
+ *
+ */
 class CourseCredits extends Local_Db_CourseTableMany
 {
     protected static $_instance;
@@ -24,7 +29,7 @@ class CourseCredits extends Local_Db_CourseTableMany
         $me = self::getInstance();
         $config = array('db'    => $me->_db,
                         'table' => $me,
-                        'data'  => array('course_generation' => $course->id));
+                        'data'  => array('course_generation' => $course->getPrimaryKey()));
         $newRecord = new CourseCredit($config);
         return $newRecord;
     }
diff --git a/application/models/tables/CourseCrosslistings.php b/application/models/tables/CourseCrosslistings.php
index 7db605295d454a6fc04357275c571b317d8a09c0..f36debd2993314222676de16ad241b2dac1038d7 100644
--- a/application/models/tables/CourseCrosslistings.php
+++ b/application/models/tables/CourseCrosslistings.php
@@ -1,5 +1,10 @@
 <?php
 
+/**
+ *
+ * @primary course_crosslisting_id
+ *
+ */
 class CourseCrosslistings extends Local_Db_CourseTableMany
 {
     protected static $_instance;
@@ -32,15 +37,20 @@ class CourseCrosslistings extends Local_Db_CourseTableMany
         $me = self::getInstance();
         $db = $me->getAdapter();
 
+        $courseCrosslistingsPrimaryKeyName = $me->getPrimaryKeyName();
+        $courseCodePrimaryKeyName = CourseCodes::getInstance()->getPrimaryKeyName();
+        $courseGenerationsPrimaryKeyName = CourseGenerations::getInstance()->getPrimaryKeyName();
+        $assetsPrimaryKeyName = Assets::getInstance()->getPrimaryKeyName();
+
         $select = $db->select();
         $select->from('creq_course_codes');
         $select->join('creq_course_crosslistings',
-                      'creq_course_codes.id = creq_course_crosslistings.course_code',
-                      'id');
+                      'creq_course_codes.' . $courseCodePrimaryKeyName . ' = creq_course_crosslistings.course_code',
+                      $courseCrosslistingsPrimaryKeyName);
         $select->join('creq_course_generations',
-                      'creq_course_crosslistings.generation = creq_course_generations.id');
+                      'creq_course_crosslistings.generation = creq_course_generations.' . $courseGenerationsPrimaryKeyName);
         $select->join('creq_assets',
-                      'creq_course_generations.asset_id = creq_assets.id');
+                      'creq_course_generations.asset_id = creq_assets.' . $assetsPrimaryKeyName);
         $select->where('creq_course_generations.type = "official"');
         $select->where($db->quoteInto('subject = ?', $subject));
         $select->where($db->quoteInto('course_number = ?', $number));
@@ -48,10 +58,10 @@ class CourseCrosslistings extends Local_Db_CourseTableMany
         $select->order('creation_time DESC');
 
         $row = $db->fetchRow($select);
-        $result = $me->find($row['id']);
+        $result = $me->find($row[$courseCrosslistingsPrimaryKeyName]);
 
 
-        if($result->id == '') {
+        if($result instanceof Nmc_Db_Table_Row && $result->getPrimaryKey() == '') {
             return null;
         } else {
             return $result;
@@ -66,7 +76,7 @@ class CourseCrosslistings extends Local_Db_CourseTableMany
         $where = $db->quoteInto('course_code = ?', $courseCodeId);
         $self->fetchRow($where);
 
-        if($result->id == '') {
+        if($result->getPrimaryKey() == '') {
             return null;
         } else {
             return $result;
@@ -78,7 +88,7 @@ class CourseCrosslistings extends Local_Db_CourseTableMany
         $me = self::getInstance();
         $config = array('db'    => $me->_db,
                         'table' => $me,
-                        'data'  => array('parent_course' => $course->id));
+                        'data'  => array('parent_course' => $course->getPrimaryKey()));
         $newRecord = new CourseCrosslisting($config);
         return $newRecord;
     }
@@ -120,5 +130,3 @@ class CourseCrosslistings extends Local_Db_CourseTableMany
         return $rowset;
     }
 }
-
-?>
\ No newline at end of file
diff --git a/application/models/tables/CourseDetails.php b/application/models/tables/CourseDetails.php
index a0debddc09e1102c1ec3c37212b992e00d904bd3..dd41fa8837f770289a327e0faa89cbfe32f70e26 100644
--- a/application/models/tables/CourseDetails.php
+++ b/application/models/tables/CourseDetails.php
@@ -1,5 +1,10 @@
 <?php
 
+/**
+ *
+ * @primary course_detail_id
+ *
+ */
 class CourseDetails extends Local_Db_CourseTableOne
 {
     protected static $_instance;
diff --git a/application/models/tables/CourseDfRemovals.php b/application/models/tables/CourseDfRemovals.php
index 3eba430e6c29162a2187a67f7b54bd3ccdb42412..5ee9df10897a181f4188e762f520555c84e8e770 100644
--- a/application/models/tables/CourseDfRemovals.php
+++ b/application/models/tables/CourseDfRemovals.php
@@ -1,5 +1,10 @@
 <?php
 
+/**
+ *
+ * @primary course_df_removal_id
+ *
+ */
 class CourseDfRemovals extends Local_Db_CourseTableMany
 {
     protected static $_instance;
@@ -24,7 +29,7 @@ class CourseDfRemovals extends Local_Db_CourseTableMany
         $me = self::getInstance();
         $config = array('db'    => $me->_db,
                         'table' => $me,
-                        'data'  => array('course_generation_passed' => $course->id));
+                        'data'  => array('course_generation_passed' => $course->getPrimaryKey()));
         $newRecord = new CourseCredit($config);
         return $newRecord;
     }
diff --git a/application/models/tables/CourseGenerations.php b/application/models/tables/CourseGenerations.php
index 0be838410041458ff776b7f76c028d2ce51f90b8..2a292428338f8696730c115d2e2d61bb02f81145 100644
--- a/application/models/tables/CourseGenerations.php
+++ b/application/models/tables/CourseGenerations.php
@@ -1,5 +1,10 @@
 <?php
 
+/**
+ *
+ * @primary course_generation_id
+ *
+ */
 class CourseGenerations extends Nmc_Db_Table
 {
     protected static $_instance;
@@ -33,7 +38,7 @@ class CourseGenerations extends Nmc_Db_Table
         $me = CourseGenerations::getInstance();
 
         $row = CourseCrosslistings::fetchBySubjectNumberLetter($subject, $number, $letter);
-        if(!$row->id) {
+        if(!$row->getPrimaryKey()) {
             return null;
         }
 
diff --git a/application/models/tables/CourseGradTieIns.php b/application/models/tables/CourseGradTieIns.php
index cda84a48e3598e7ddacf1a45d5377c218d01806e..b070beee639463ce20decd0edf7d15a77ca0568c 100644
--- a/application/models/tables/CourseGradTieIns.php
+++ b/application/models/tables/CourseGradTieIns.php
@@ -1,5 +1,10 @@
 <?php
 
+/**
+ *
+ * @primary course_grad_tie_in_id
+ *
+ */
 class CourseGradTieIns extends Local_Db_CourseTableOne
 {
     protected static $_instance;
diff --git a/application/models/tables/CoursePrerequisites.php b/application/models/tables/CoursePrerequisites.php
index 88b445f2889a2ae11d1101952dd4aed891fa4069..2f523f1db98c4fce2b5944bde8a0d1c781a39b7f 100644
--- a/application/models/tables/CoursePrerequisites.php
+++ b/application/models/tables/CoursePrerequisites.php
@@ -1,5 +1,10 @@
 <?php
 
+/**
+ *
+ * @primary course_prerequisite_id
+ *
+ */
 class CoursePrerequisites extends Local_Db_CourseTableMany
 {
     protected static $_instance;
@@ -24,7 +29,7 @@ class CoursePrerequisites extends Local_Db_CourseTableMany
         $me = self::getInstance();
         $config = array('db'    => $me->_db,
                         'table' => $me,
-                        'data'  => array('parent_course' => $course->id));
+                        'data'  => array('parent_course' => $course->getPrimaryKey()));
         $newRecord = new CourseCredit($config);
         return $newRecord;
     }
diff --git a/application/models/tables/Courses.php b/application/models/tables/Courses.php
index 023d9ee47aedd82a08eee41840b20b7e4e90d742..25e6cf0a3b2de8d0d0046cc7c5292293e2a581c2 100644
--- a/application/models/tables/Courses.php
+++ b/application/models/tables/Courses.php
@@ -1,5 +1,10 @@
 <?php
 
+/**
+ *
+ * @primary course_id
+ *
+ */
 class Courses extends Nmc_Db_Table
 {
     protected static $_instance;
diff --git a/application/models/tables/Departments.php b/application/models/tables/Departments.php
new file mode 100644
index 0000000000000000000000000000000000000000..fdcb8946a1b2325242ae906df85024b0f867f89e
--- /dev/null
+++ b/application/models/tables/Departments.php
@@ -0,0 +1,40 @@
+<?php
+
+/**
+ *
+ * @primary department_id
+ *
+ */
+class Departments extends Nmc_Db_Table
+{
+
+    //protected $_rowClass = 'Department';
+
+    /**
+     * The one true instance
+     *
+     * @var Departments
+     */
+    protected static $_instance = null;
+
+    /**
+     * Returns the one true instance
+     *
+     * @return Departments
+     */
+    public static function getInstance($config = array())
+    {
+        if(!self::$_instance) {
+            self::$_instance = new Departments($config);
+        }
+        return self::$_instance;
+    }
+
+    public function fetchByName($departmentName)
+    {
+        $where = $this->_db->quoteInto('name = ?', $departmentName);
+        $row = $this->fetchRow($where);
+        return $row;
+    }
+
+}
\ No newline at end of file
diff --git a/application/models/tables/Files.php b/application/models/tables/Files.php
index ef97892544640bb1214cad3928d6252845276a89..b754daa5b1aae096f4f4e7501f3612a263e9363c 100644
--- a/application/models/tables/Files.php
+++ b/application/models/tables/Files.php
@@ -1,5 +1,10 @@
 <?php
 
+/**
+ *
+ * file_id
+ *
+ */
 class Files extends Nmc_Db_Table
 {
     static protected $_instance;
diff --git a/application/models/tables/GroupImpliedMemberships.php b/application/models/tables/GroupImpliedMemberships.php
new file mode 100644
index 0000000000000000000000000000000000000000..ccb83b4dcdf33d5231cfef65b69a01c3da3b3bc2
--- /dev/null
+++ b/application/models/tables/GroupImpliedMemberships.php
@@ -0,0 +1,154 @@
+<?php
+
+/**
+ *
+ * @primary group_implied_membership_id
+ *
+ */
+class GroupImpliedMemberships extends Nmc_Db_Table
+{
+    protected $_rowClass = 'GroupImpliedMembership';
+    protected static $_instance = null;
+
+    /**
+     * Return the one true instance
+     *
+     * @param array[optional] $config
+     * @return GroupImpliedMemberships
+     */
+    public static function getInstance($config = array())
+    {
+        if(!self::$_instance) {
+            self::$_instance = new GroupImpliedMemberships($config);
+        }
+        return self::$_instance;
+    }
+
+    public function fetchNew()
+    {
+        throw new Zend_Db_Exception('Cannot manually edit implied memberships!');
+    }
+
+    /**
+     * Returns a rowset of all of the Groups the User belongs to
+     *
+     * @param Person $user
+     * @param bool[optional] $implied
+     * @param bool[optional] $explicit
+     * @return Nmc_Db_Table_Rowset
+     */
+    public function fetchGroupsByUser(Person $user, $implied = true, $explicit = true)
+    {
+        $primaryGroup = Groups::getInstance()->find($user->primaryGroup);
+        return $this->fetchGroupsByChildGroup($primaryGroup, $implied, $explicit);
+    }
+
+    /**
+     * Return a rowset of groups that the specified group is a member of
+     *
+     * @param Group $group
+     * @param bool[optional] $implied
+     * @param bool[optional] $explicit
+     * @return Nmc_Db_Table_Rowset
+     */
+    public function fetchGroupsByChildGroup(Group $group, $implied = true, $explicit = true)
+    {
+        $db = $this->_db;
+        $where = array();
+        $where[] = $db->quoteInto('child_group = ?', $group->getPrimaryKey());
+        if(!$implied && $explicit) {
+            $where[] = "explicit = 'yes'";
+        } else if($implied && !$explicit) {
+            $where[] = "explicit = 'no'";
+        } else if(!$implied && !$explicit) {
+            $where[] = 'false';
+        }
+
+        $where = implode(' AND ', $where);
+        $groupIdSet = $this->fetchAll($where);
+
+        $groupIds = array();
+        foreach($groupIdSet as $row) {
+            $groupIds[] = $row->parentGroup;
+        }
+
+        $groupIds[] = -1;
+        $groupPrimaryKeyName = Groups::getInstance()->getPrimaryKeyName();
+        $where = $db->quoteInto('`' . $groupPrimaryKeyName . '` IN(?)', $groupIds);
+        $groups = Groups::getInstance()->fetchAllWithoutPrimaries($where);
+        return $groups;
+    }
+
+    /**
+     * Return a rowset of groups that belong to the specified group.
+     *
+     * @param Group $group
+     * @param bool[optional] $implied
+     * @param bool[optional] $explicit
+     * @return Nmc_Db_Table_Rowset
+     */
+    public function fetchGroupsByParentGroup(Group $group, $implied = true, $explicit = true)
+    {
+        $db = $this->_db;
+        $where = array();
+        $where[] = $db->quoteInto('parent_group = ?', $group->getPrimaryKey());
+        if(!$implied && $explicit) {
+            $where[] = "explicit = 'yes'";
+        } else if($implied && !$explicit) {
+            $where[] = "explicit = 'no'";
+        } else if(!$implied && !$explicit) {
+            $where[] = 'false';
+        }
+
+        $where = implode(' AND ', $where);
+        $groupIdSet = $this->fetchAll($where);
+
+        $groupIds = array();
+        foreach($groupIdSet as $row) {
+            $groupIds[] = $row->childGroup;
+        }
+
+        $groupIds[] = -1;
+        $where = $db->quoteInto('id IN(?)', $groupIds);
+        $groups = Groups::getInstance()->fetchAllWithoutPrimaries($where);
+        return $groups;
+    }
+
+    /**
+     * Return a rowset of users that belong to the specified group.
+     *
+     * @param Group $group
+     * @param bool[optional] $implied
+     * @param bool[optional] $explicit
+     * @return Nmc_Db_Table_Rowset
+     */
+    public function fetchUsersByParentGroup(Group $group, $implied = true, $explicit = true)
+    {
+        $usersTable = Users::getInstance();
+        $usersTableInfo = $usersTable->info();
+        $usersTableName = $usersTableInfo['name'];
+        $usersTablePrimary = $usersTableInfo['primary'];
+
+        $select = $this->_db->select();
+        $select->from($usersTableName, $usersTablePrimary);
+        $select->join($this->_name,
+                      $usersTableName . '.primary_group = ' . $this->_name . '.child_group');
+        $select->where($this->_db->quoteInto('parent_group = ?', $group->getPrimaryKey()));
+        if(!$implied && $explicit) {
+            $select->where("explicit = 'yes'");
+        } else if($implied && !$explicit) {
+            $select->where("explicit = 'no'");
+        } else if(!$implied && !$explicit) {
+            $select->where('false');
+        }
+        $select->distinct();
+        $userIds = $this->_db->fetchCol($select);
+
+        $userIds[] = -1;
+        $where = $this->_db->quoteInto('id IN(?)', $userIds);
+        $users = Users::getInstance()->fetchAll($where);
+        return $users;
+    }
+}
+
+?>
\ No newline at end of file
diff --git a/application/models/tables/GroupMemberships.php b/application/models/tables/GroupMemberships.php
new file mode 100644
index 0000000000000000000000000000000000000000..43d587f8684ad98c539bfd023fe3263516e8ef1a
--- /dev/null
+++ b/application/models/tables/GroupMemberships.php
@@ -0,0 +1,27 @@
+<?php
+
+/**
+ *
+ * @primary group_membership_id
+ *
+ */
+class GroupMemberships extends Nmc_Db_Table
+{
+    protected static $_instance = null;
+
+    /**
+     * Return the one true instance
+     *
+     * @param array[optional] $config
+     * @return GroupMemberships
+     */
+    public static function getInstance($config = array())
+    {
+        if(!self::$_instance) {
+            self::$_instance = new GroupMemberships($config);
+        }
+        return self::$_instance;
+    }
+}
+
+?>
\ No newline at end of file
diff --git a/application/models/tables/Groups.php b/application/models/tables/Groups.php
new file mode 100644
index 0000000000000000000000000000000000000000..02996805ed4f3146e474c81452da83e1df821d62
--- /dev/null
+++ b/application/models/tables/Groups.php
@@ -0,0 +1,65 @@
+<?php
+
+/**
+ *
+ * @primary group_id
+ *
+ */
+class Groups extends Nmc_Db_Table
+{
+    protected $_rowClass = 'Group';
+    /**
+     * The one true instance
+     *
+     * @var Groups
+     */
+    protected static $_instance = null;
+
+    /**
+     * Returns the one true instance
+     *
+     * @param array[optional] $config
+     * @return Groups
+     */
+    public static function getInstance($config = array())
+    {
+        if(!self::$_instance) {
+            self::$_instance = new Groups($config);
+        }
+        return self::$_instance;
+    }
+
+    /**
+     * Fetches the group with the given name
+     *
+     * @param string $groupName
+     * @return Group
+     */
+    public function fetchByName($groupName)
+    {
+        $where = $this->_db->quoteInto('name = ?', $groupName);
+        $row = $this->fetchRow($where);
+        return $row;
+    }
+
+    public function fetchAllWithoutPrimaries($where = null, $order = null, $count = null, $offset = null)
+    {
+        $db = $this->_db;
+        $select = $db->select();
+        $userTableInfo = Users::getInstance()->info();
+        $userTableName = $userTableInfo['name'];
+        $select->from($userTableName, 'primary_group');
+        $select->distinct();
+        $primaryGroupIds = $db->fetchCol($select);
+
+        $extra_where = $db->quoteInto($this->_name . '.' . $this->_primary . ' NOT IN(?)', $primaryGroupIds);
+        if($where) {
+            $where .= ' AND ';
+        }
+        $where .= $extra_where;
+
+        return $this->fetchAll($where, $order, $count, $offset);
+    }
+}
+
+?>
\ No newline at end of file
diff --git a/application/models/tables/People.php b/application/models/tables/People.php
index 299bcc104e7856eba025da7e58d835e644f0c27e..c684b19382080118e7a8c647ce476e9b75dbc4c5 100644
--- a/application/models/tables/People.php
+++ b/application/models/tables/People.php
@@ -1,5 +1,10 @@
 <?php
 
+/**
+ *
+ * @primary person_id
+ *
+ */
 class People extends Nmc_Db_Table
 {
     static private $_instance;
@@ -29,9 +34,36 @@ class People extends Nmc_Db_Table
     static public function findByUserName($userName)
     {
         $me = self::getInstance();
-        $where = $me->_getDefaultAdapter()->quoteInto('user_name=?', $userName);
+        $db = $me->_getDefaultAdapter();
+        $inflector = new Zend_Db_Inflector();
+
+        $usersTable = Users::getInstance();
+        $usersPrimaryKey = $usersTable->getPrimaryKeyName();
+        $usersPrimaryKey = $inflector->camelize($usersPrimaryKey);
+
+
+        $where = $db->quoteInto('user_name=?', $userName);
+        $user = $usersTable->fetchRow($where);
+
+        $where = $db->quoteInto('user_id=?', $user->$usersPrimaryKey);
         return $me->fetchRow($where);
     }
+
+    public function findByUser(User $user)
+    {
+        return $this->findByUserId($user->getPrimaryKey());
+    }
+
+    public function findByUserId($userId)
+    {
+        if(is_array($userId)) {
+            $where = $this->_db->quoteInto('user_id IN (?)', $userId);
+            return $this->fetchAll($where);
+        } else {
+            $where = $this->_db->quoteInto('user_id=?', $userId);
+            return $this->fetchRow($where);
+        }
+    }
 }
 
 ?>
\ No newline at end of file
diff --git a/application/models/tables/RequestFiles.php b/application/models/tables/RequestFiles.php
index 352d7bb99b49d61322e0cce7c03b0e7fa8dbc489..c65f2d8f35ecaccf52e273508013b932623189e5 100644
--- a/application/models/tables/RequestFiles.php
+++ b/application/models/tables/RequestFiles.php
@@ -1,5 +1,10 @@
 <?php
 
+/**
+ *
+ * @primary request_file_id
+ *
+ */
 class RequestFiles extends Nmc_Db_Table
 {
     static protected $_instance;
@@ -15,7 +20,7 @@ class RequestFiles extends Nmc_Db_Table
 
     public function getByRequest(Request $request)
     {
-        $where = $this->getAdapter()->quoteInto('request = ?', $request->id);
+        $where = $this->getAdapter()->quoteInto('request = ?', $request->getPrimaryKey());
         return $this->fetchAll($where);
     }
 }
diff --git a/application/models/tables/RequestStacks.php b/application/models/tables/RequestStacks.php
index ec46684799935d3a44ea16f6f79886cafa63aa0b..1b583107d90e57fbb2e27a15e6d7cf04a7a613c6 100644
--- a/application/models/tables/RequestStacks.php
+++ b/application/models/tables/RequestStacks.php
@@ -1,5 +1,10 @@
 <?php
 
+/**
+ *
+ * @primary request_stack_id
+ *
+ */
 class RequestStacks extends Nmc_Db_Table {}
 
 ?>
\ No newline at end of file
diff --git a/application/models/tables/RequestTypes.php b/application/models/tables/RequestTypes.php
index b277de2445bb474fff6f3d52955f8f5b2ae5a53d..60c081a2fac98f6b4965fae3364b8d58eff2c0a2 100644
--- a/application/models/tables/RequestTypes.php
+++ b/application/models/tables/RequestTypes.php
@@ -1,5 +1,10 @@
 <?php
 
+/**
+ *
+ * @primary request_type_id
+ *
+ */
 class RequestTypes extends Nmc_Db_Table
 {
     private static $_instance;
@@ -10,7 +15,7 @@ class RequestTypes extends Nmc_Db_Table
         parent::__construct($config);
         $contents = $this->fetchAll();
         foreach($contents as $row) {
-            $this->_map[$row->name] = $row->id;
+            $this->_map[$row->name] = $row->getPrimaryKey();
         }
     }
 
diff --git a/application/models/tables/Requests.php b/application/models/tables/Requests.php
index 460a46a9b030feeb7ad47ef3ee42f7095ec38f5f..f669f12ec7285c53ffc21858b79be79e2c98bfd4 100644
--- a/application/models/tables/Requests.php
+++ b/application/models/tables/Requests.php
@@ -1,5 +1,10 @@
 <?php
 
+/**
+ *
+ * @primary request_id
+ *
+ */
 class Requests extends Nmc_Db_Table {
 
     protected static $_instance;
@@ -20,7 +25,12 @@ class Requests extends Nmc_Db_Table {
 
     public function getRequestsForUser(Person $user)
     {
-        $where = $this->_db->quoteInto('owner = ?', $user->id);
+        $usersTable = Users::getInstance();
+        $usersPrimaryKey = $usersTable->getPrimaryKeyName();
+        $inflector = new Zend_Db_Inflector();
+        $usersPrimaryKey = $inflector->camelize($usersPrimaryKey);
+
+        $where = $this->_db->quoteInto('owner = ?', $user->$usersPrimaryKey);
         $requests = $this->fetchAll($where);
         return $requests;
     }
diff --git a/application/views/approval_body_admin.xhtml b/application/views/approval_body_admin.xhtml
new file mode 100644
index 0000000000000000000000000000000000000000..e7e6dd5c63c95f1bd32f80c33bf6629bccae0aa8
--- /dev/null
+++ b/application/views/approval_body_admin.xhtml
@@ -0,0 +1,65 @@
+<div id="admin_list">
+    <h2>Approval Bodies</h2>
+    <ul>
+        <?php foreach($this->approvalBodies as $approvalBody) { ?>
+        <li>
+            <a href="/ApprovalBodyAdmin/EditBody/<?php echo $approvalBody->getPrimaryKey(); ?>">
+                <?php echo $approvalBody->name; ?>
+            </a>
+            <ul>
+                <li>
+                    <a href="/ApprovalBodyAdmin/AddRole/<?php echo $approvalBody->getPrimaryKey(); ?>">
+                        --Add New Role--
+                    </a>
+                </li>
+                <?php foreach($approvalBody->roles as $role) { ?>
+                <li>
+                    <a href="/ApprovalbodyAdmin/EditRole/<?php echo $role->getPrimaryKey(); ?>">
+                        <?php echo $role->name; ?>
+                    </a>
+                </li>
+                <?php } ?>
+            </ul>
+        </li>
+        <?php } ?>
+    </ul>
+</div>
+
+<?php if($this->approvalBody) { ?>
+<div id="edit_user" class="edit_pane">
+    <h2>Editing Approval Body: <?php echo $this->approvalBody->name; ?></h2>
+    <form action="/ApprovalBodyAdmin/EditBodyPost/<?php echo $this->approvalBody->getPrimaryKey(); ?>" method="post">
+        <label for="name">Name:</label>
+        <?php echo $this->formText('name',
+                                   $this->approvalBody->name,
+                                   array('size' => 32)); ?>
+        <label for="description">Description:</label>
+        <?php echo $this->formText('description',
+                                   $this->approvalBody->description,
+                                   array('size' => 32)); ?>
+        <?php echo $this->formSubmit('Submit', 'Submit'); ?>
+    </form>
+</div>
+<?php } ?>
+
+<?php if($this->approvalBodyRole) { ?>
+<div id="edit_user" class="edit_pane">
+    <h2>Editing Approval Body Role: <?php echo $this->approvalBodyRole->name; ?></h2>
+    <?php if($this->approvalBodyRole->getPrimaryKey()) { ?>
+    <form action="/ApprovalBodyAdmin/EditRolePost/<?php echo $this->approvalBodyRole->getPrimaryKey(); ?>" method="post">
+    <?php } else { ?>
+    <form action="/ApprovalBodyAdmin/EditRolePost/-<?php echo $this->approvalBodyRole->approvalBody; ?>" method="post">
+    <?php } ?>
+        <label for="name">Name:</label>
+        <?php echo $this->formText('name',
+                                   $this->approvalBodyRole->name,
+                                   array('size' => 32)); ?>
+        <label for="group">User/Group:</label>
+        <?php echo $this->formSelect('group',
+                                     $this->approvalBodyRole->group,
+                                     null,
+                                     $this->groups->columnToArray('name', 'id')); ?>
+        <?php echo $this->formSubmit('Submit', 'Submit'); ?>
+    </form>
+</div>
+<?php } ?>
\ No newline at end of file
diff --git a/application/views/conflict.xhtml b/application/views/conflict.xhtml
index a8cbd43c55989e4c5c5e3a677c65184a6283cda1..13b0ffdbc0e119e62611b40c69a82297aef2278d 100644
--- a/application/views/conflict.xhtml
+++ b/application/views/conflict.xhtml
@@ -9,11 +9,11 @@
 
 <ul id="selections">
     <?php foreach($this->dups as $dup) { ?>
-    <li id="<?php echo $dup->id; ?>">
+    <li id="<?php echo $dup->getPrimaryKey(); ?>">
         <?php __bulletinPreview($dup); ?>
         <div class="choices">
-            <a class="delete" href="/conflict/remove/<?php echo $dup->id; ?>">Delete</a>
-            <a class="edit" href="/CourseAdmin/index/<?php echo $dup->id; ?>">Edit</a>
+            <a class="delete" href="/conflict/remove/<?php echo $dup->getPrimaryKey(); ?>">Delete</a>
+            <a class="edit" href="/CourseAdmin/index/<?php echo $dup->getPrimaryKey(); ?>">Edit</a>
         </div>
     </li>
     <?php } ?>
diff --git a/application/views/editAddress.xhtml b/application/views/editAddress.xhtml
index c64f937f6153c72eca777f840105f72ab6901b82..17a26a04cbff132f15942127f2da9853e2411ed3 100644
--- a/application/views/editAddress.xhtml
+++ b/application/views/editAddress.xhtml
@@ -1,5 +1,5 @@
 <form action="/testform/save" method="post">
-    <input type="hidden" name="id" value="<?php echo $this->record->id; ?>" />
+    <input type="hidden" name="id" value="<?php echo $this->record->getPrimaryKey(); ?>" />
     First Name:<br />
     <input type="text" name="firstName" value="<?php echo $this->record->firstName; ?>" /><br />
     Last Name:<br />
diff --git a/application/views/edit_course.xhtml b/application/views/edit_course.xhtml
index 21a932dfa648894b3cf1895467a0f118766a9403..becb35a2850571197f6056faae9a4948df6dc87b 100644
--- a/application/views/edit_course.xhtml
+++ b/application/views/edit_course.xhtml
@@ -3,7 +3,7 @@
 
 <form action="/courseadmin/updatecourse/<?php echo implode('/', $this->uriParams); ?>" method="post">
 
-<input type="hidden" name="courseId" value="<?php echo $this->course->id; ?>" />
+<input type="hidden" name="courseId" value="<?php echo $this->course->getPrimaryKey(); ?>" />
 
 <h2>Main Details</h2>
 <fieldset>
@@ -403,17 +403,17 @@
     <?php foreach($this->course->dfRemovals as $key => $dfRemoval) { ?>
     <tr>
         <td>
-            <input type="text" name="crosslistings[<?php echo $dfRemoval->id; ?>][subject]" value="<?php echo $crosslisting->subject; ?>" />
+            <input type="text" name="crosslistings[<?php echo $dfRemoval->getPrimaryKey(); ?>][subject]" value="<?php echo $crosslisting->subject; ?>" />
         </td>
         <td>
-            <input type="text" name="crosslistings[<?php echo $dfRemoval->id; ?>][courseNumber]" value="<?php echo $crosslisting->courseNumber; ?>" />
+            <input type="text" name="crosslistings[<?php echo $dfRemoval->getPrimaryKey(); ?>][courseNumber]" value="<?php echo $crosslisting->courseNumber; ?>" />
         </td>
         <td>
-            <input type="text" name="crosslistings[<?php echo $dfRemoval->id; ?>][courseLetter]" value="<?php echo $crosslisting->courseLetter; ?>" />
+            <input type="text" name="crosslistings[<?php echo $dfRemoval->getPrimaryKey(); ?>][courseLetter]" value="<?php echo $crosslisting->courseLetter; ?>" />
         </td>
         <td>
-            <input type="hidden" name="dfRemovals[<?php echo $dfRemoval->id; ?>][delete]" value="no" />
-            <input type="checkbox" name="dfRemovals[<?php echo $dfRemoval->id; ?>][delete]" value="yes" />
+            <input type="hidden" name="dfRemovals[<?php echo $dfRemoval->getPrimaryKey(); ?>][delete]" value="no" />
+            <input type="checkbox" name="dfRemovals[<?php echo $dfRemoval->getPrimaryKey(); ?>][delete]" value="yes" />
         </td>
     </tr>
     <?php } ?>
@@ -432,17 +432,17 @@
     <?php foreach($this->course->prerequisites as $key => $prerequisite) { ?>
     <tr>
         <td>
-            <input type="text" name="crosslistings[<?php echo $prerequisite->id; ?>][subject]" value="<?php echo $crosslisting->subject; ?>" />
+            <input type="text" name="crosslistings[<?php echo $prerequisite->getPrimaryKey(); ?>][subject]" value="<?php echo $crosslisting->subject; ?>" />
         </td>
         <td>
-            <input type="text" name="crosslistings[<?php echo $prerequisite->id; ?>][courseNumber]" value="<?php echo $crosslisting->courseNumber; ?>" />
+            <input type="text" name="crosslistings[<?php echo $prerequisite->getPrimaryKey(); ?>][courseNumber]" value="<?php echo $crosslisting->courseNumber; ?>" />
         </td>
         <td>
-            <input type="text" name="crosslistings[<?php echo $prerequisite->id; ?>][courseLetter]" value="<?php echo $crosslisting->courseLetter; ?>" />
+            <input type="text" name="crosslistings[<?php echo $prerequisite->getPrimaryKey(); ?>][courseLetter]" value="<?php echo $crosslisting->courseLetter; ?>" />
         </td>
         <td>
-            <input type="hidden" name="prerequisites[<?php echo $prerequisite->id; ?>][delete]" value="no" />
-            <input type="checkbox" name="prerequisites[<?php echo $prerequisite->id; ?>][delete]" value="yes" />
+            <input type="hidden" name="prerequisites[<?php echo $prerequisite->getPrimaryKey(); ?>][delete]" value="no" />
+            <input type="checkbox" name="prerequisites[<?php echo $prerequisite->getPrimaryKey(); ?>][delete]" value="yes" />
         </td>
     </tr>
     <?php } ?>
@@ -452,7 +452,7 @@
 
 </form>
 
-<form id="delete_course" method="post" action="/CourseAdmin/DeleteCourse/<?php echo $this->course->id; ?>">
+<form id="delete_course" method="post" action="/CourseAdmin/DeleteCourse/<?php echo $this->course->getPrimaryKey(); ?>">
     <label>
         Enable Delete
         <input type="checkbox" id="enable_delete" />
diff --git a/application/views/listAddress.xhtml b/application/views/listAddress.xhtml
index 19df2d2c70d76f3aea52537fbcc8674572366b1d..faabdc8bd7ab71952cb307ca168024d98b49dc99 100644
--- a/application/views/listAddress.xhtml
+++ b/application/views/listAddress.xhtml
@@ -13,7 +13,7 @@
         <td><?php echo $record->phone; ?></td>
         <td><?php echo $record->address; ?></td>
         <td><?php echo $record->zip; ?></td>
-        <td><a href="/testform/edit/<?php echo $record->id; ?>">Edit</a></td>
+        <td><a href="/testform/edit/<?php echo $record->getPrimaryKey(); ?>">Edit</a></td>
     </tr>
     <?php } ?>
 </table>
\ No newline at end of file
diff --git a/application/views/login.xhtml b/application/views/login.xhtml
index 1b1eba2b386cb9f454d9427fbd18fbc68413424d..79674b6eaf2870e05ae60815d75bf2357109697c 100644
--- a/application/views/login.xhtml
+++ b/application/views/login.xhtml
@@ -26,18 +26,11 @@
         <div class="bl"></div>
     </div>
     <div class="content">
-        Heading...<br />
+        <h1>Gail!</h1>
         <br />
-        Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Fusce nisi
-        nulla, auctor ut, ultricies at, varius vitae, diam. Vestibulum dictum,
-        mi ac gravida porttitor, mauris justo molestie mauris, in aliquam dolor
-        tortor vitae metus. Pellentesque urna. Aliquam sagittis tortor vitae mi.
-        Integer eleifend, purus sit amet pharetra sagittis, lectus neque
-        molestie pede, non iaculis massa ligula ut erat. Sed risus ligula, porta
-        eu, nonummy quis, sollicitudin eu, libero. Fusce facilisis sodales
-        mauris. Curabitur egestas molestie eros. Nam ligula felis, sodales et,
-        facilisis vitae, ultrices eget, diam. Maecenas tristique elementum pede.
-        Suspendisse vel lectus.
+        <a href="http://creqdev.unl.edu">creqdev.unl.edu</a> is now running up to date code.
+        Please use it from now on.
+        CourseAdmin editting works just as it always has.
     </div>
 </div>
 
diff --git a/application/views/my_home.xhtml b/application/views/my_home.xhtml
index eb13f35de3493edefcd5ad02a937bbdd21db8f29..1631d1d866ab4bf86e9c368b40b21ab34a1f8f07 100644
--- a/application/views/my_home.xhtml
+++ b/application/views/my_home.xhtml
@@ -17,12 +17,12 @@
         <div class="tr"></div>
         <div class="tl">
             <h2>Activity</h2>
-            <h3><em>Curent Roll:</em> Instructor</h3>
+            <h3><em>Curent Roll:</em> Yourself</h3>
         </div>
         <div class="bl"></div>
     </div>
     <div class="content">
-        <?php if($this->requests->count() == 0) { ?>
+        <?php if($this->myRequests->count() == 0) { ?>
         <h2>You currently have no requests.</h2>
         <?php } else { ?>
         <table class="course_list">
@@ -36,7 +36,7 @@
             </tr>
             <?php
             $row = 0;
-            foreach($this->requests as $request) {
+            foreach($this->myRequests as $request) {
                 $course = $request->getCourseGeneration();
             ?>
             <tr <?php echo (++$row % 2 ? 'class="odd"' : ''); ?>>
@@ -47,7 +47,7 @@
                 <td><?php echo $request->type; ?></td>
                 <td><select><option>Submission</option></select></td>
                 <td>
-                    <a href="/NewRequest/Load/<?php echo $request->id; ?>">
+                    <a href="/NewRequest/Load/<?php echo $request->getPrimaryKey(); ?>">
                         View/Edit
                     </a>
                 </td>
@@ -56,6 +56,52 @@
         </table>
         <?php } ?>
     </div>
+
+    <?php foreach($this->roles as $role) { ?>
+    <div class="box_shadow_2">
+        <div class="tr"></div>
+        <div class="tl">
+            <h2>Activity</h2>
+            <h3><em>Curent Roll:</em> <?php echo $role->name; ?></h3>
+        </div>
+        <div class="bl"></div>
+    </div>
+    <div class="content">
+        <?php if($role->getRequests()->count() == 0) { ?>
+        <h2>This role currently has no requests.</h2>
+        <?php } else { ?>
+        <table class="course_list">
+            <tr>
+                <th id="check">&#160;</th>
+                <th id="course">Course</th>
+                <th id="college">College</th>
+                <th id="type">Type</th>
+                <th id="status">Status</th>
+                <th id="view_edit">View/Edit</th>
+            </tr>
+            <?php
+            $row = 0;
+            foreach($role->getRequests() as $request) {
+                $course = $request->getCourseGeneration();
+            ?>
+            <tr <?php echo (++$row % 2 ? 'class="odd"' : ''); ?>>
+                <td><input type="checkbox" /></td>
+                <td><?php echo $request->getCourseGeneration()->subject . ' '
+                             . $request->getCourseGeneration()->courseNumber; ?></td>
+                <td>NONC</td>
+                <td><?php echo $request->type; ?></td>
+                <td><select><option>Submission</option></select></td>
+                <td>
+                    <a href="/NewRequest/Load/<?php echo $request->getPrimaryKey(); ?>">
+                        View/Edit
+                    </a>
+                </td>
+            </tr>
+            <?php } ?>
+        </table>
+        <?php } ?>
+    </div>
+    <?php } ?>
 </div>
 
 
diff --git a/application/views/user_admin.xhtml b/application/views/user_admin.xhtml
new file mode 100644
index 0000000000000000000000000000000000000000..957cef653cda2f5a324cfe06a50460cad304d893
--- /dev/null
+++ b/application/views/user_admin.xhtml
@@ -0,0 +1,88 @@
+<?php function list_group(Group $group, $depth = 0) { ?>
+<h3>
+    <a href="/UserAdmin/EditGroup/<?php echo $group->getPrimaryKey(); ?>">
+        <?php echo $group->name; ?>
+    </a>
+</h3>
+<ul>
+    <?php foreach($group->getUsers(false) as $user) { ?>
+    <li>
+        <a href="/UserAdmin/EditUser/<?php echo $user->getPrimaryKey(); ?>">
+            <?php echo $user->userName; ?>
+        </a>
+    </li>
+    <?php }  ?>
+
+    <?php foreach($group->getGroups(false) as $group2) { ?>
+    <li>
+        <?php list_group($group2, $depth+1); ?>
+    </li>
+    <?php } ?>
+</ul>
+<?php } ?>
+
+<div id="admin_list">
+    <h2>Groups</h2>
+    <ul>
+        <li>
+            <a href="/UserAdmin/EditGroup/-1">
+                <h3>--Create New Group--</h3>
+            </a>
+        </li>
+        <?php foreach($this->groups as $group) { ?>
+        <li>
+            <?php if($group->isRootGroup()) {list_group($group);} ?>
+        </li>
+        <?php } ?>
+    </ul>
+</div>
+
+<?php if($this->user) { ?>
+<div id="edit_user" class="edit_pane">
+    <h2>Editing User: <?php echo $this->user->userName; ?></h2>
+    <form action="/UserAdmin/EditUser/<?php echo $this->user->getPrimaryKey(); ?>" method="post">
+        <label for="firstName">First Name:</label>
+        <?php echo $this->formText('firstName',
+                                   $this->user->firstName,
+                                   array('size' => 32)); ?>
+        <label for="lastName">Last Name:</label>
+        <?php echo $this->formText('lastName',
+                                   $this->user->lastName,
+                                   array('size' => 32)); ?>
+        <label for="groups">Groups:</label>
+        <?php echo $this->formSelect('groups',
+                                     $this->user->getGroups(false)->columnToArray('id'),
+                                     array('multiple' => 'multiple'),
+                                     $this->groups->columnToArray('name', 'id')); ?>
+        <?php echo $this->formSubmit('Submit', 'Submit'); ?>
+    </form>
+</div>
+<?php } ?>
+
+<?php if($this->group) { ?>
+<div id="edit_group" class="edit_pane">
+    <h2>Editing Group: <?php echo $this->group->name; ?></h2>
+    <form action="/UserAdmin/EditGroup/<?php echo $this->group->getPrimaryKey(); ?>" method="post">
+        <label for="delete">
+            Delete: <?php echo $this->formCheckBox('delete', null, array('id' => 'delete')); ?>
+        </label>
+        <label for="name">Name:</label>
+        <?php echo $this->formText('name', $this->group->name, array('size' => 32)); ?>
+        <label for="description">Description:</label>
+        <?php echo $this->formTextArea('description',
+                                       $this->group->description,
+                                       array('rows' => 5, 'cols' => 50)); ?>
+        <label for="groups">Groups:</label>
+        <?php echo $this->formSelect('groups',
+                                     $this->group->getGroups(false)->columnToArray('id'),
+                                     array('multiple' => 'multiple'),
+                                     $this->groups->columnToArray('name', 'id')); ?>
+        <label for="users">Users:</label>
+        <?php echo $this->formSelect('users',
+                                     $this->group->getUsers(false)->columnToArray('id'),
+                                     array('multiple' => 'multiple'),
+                                     $this->users->columnToArray('userName', 'id')); ?>
+        <?php echo $this->formSubmit('Submit', 'Submit'); ?>
+    </form>
+</div>
+<?php } ?>
\ No newline at end of file
diff --git a/document_root/css/approval_body_admin.css b/document_root/css/approval_body_admin.css
new file mode 100644
index 0000000000000000000000000000000000000000..22419919f24d2433c48f92e9ad1eca4cdf4702f4
--- /dev/null
+++ b/document_root/css/approval_body_admin.css
@@ -0,0 +1,2 @@
+
+@import url(user_admin.css);
diff --git a/document_root/css/common.oss b/document_root/css/common.oss
index c1d9a46facf2fa46a964226aab2b87eef1c00b2e..577f0a03eb7f99d7bb315b3bfdd224babc9d2855 100644
--- a/document_root/css/common.oss
+++ b/document_root/css/common.oss
@@ -19,6 +19,8 @@ ul.horizontal_menu {
     display: block;
     list-style: none;
     height: 15px;
+    margin: 0px;
+    padding: 0px;
 
     li {
         display: block;
diff --git a/document_root/css/user_admin.css b/document_root/css/user_admin.css
new file mode 100644
index 0000000000000000000000000000000000000000..660c3f0ce546c8dd66c5bb277128c46259cfeb2e
--- /dev/null
+++ b/document_root/css/user_admin.css
@@ -0,0 +1,38 @@
+#admin_list ul {
+    margin-left: 1em;
+    list-style: none;
+}
+
+div#admin_list {
+    float: left;
+}
+
+div.edit_pane {
+    margin-left: 150px;
+}
+
+div.edit_pane h2 {
+    font-size: 110%;
+    margin-bottom: 1em;
+}
+
+label, input, textarea, select {
+    display: block;
+}
+
+label {
+    margin-top: 0.5em;
+}
+
+label input {
+    display: inline;
+    margin-bottom: 0px;
+}
+
+input, textarea, select {
+    margin-bottom: 0.5em;
+}
+
+fieldset {
+    border-width: 0px
+}
\ No newline at end of file
diff --git a/document_root/javascript/test.js b/document_root/javascript/test.js
new file mode 100644
index 0000000000000000000000000000000000000000..e62aabd73a40f5f37958884f239cd99c96de6373
--- /dev/null
+++ b/document_root/javascript/test.js
@@ -0,0 +1,14 @@
+addLoadEvent( onLoadTest );
+
+function onLoadTest()
+{
+    var selections = document.getElementById('selections').getElementsByTagName('li');
+    for(var i = 0; i < selections.length; i++) {
+        selections[i].onclick = handleRemoval;
+    }
+}
+
+function handleRemoval()
+{
+    //
+}
\ No newline at end of file
diff --git a/document_root/javascript/user_admin.js b/document_root/javascript/user_admin.js
new file mode 100644
index 0000000000000000000000000000000000000000..47291e0cf416be903d8efaefe8a84c52c606acec
--- /dev/null
+++ b/document_root/javascript/user_admin.js
@@ -0,0 +1,48 @@
+addLoadEvent(onLoadUserAdmin);
+
+function onLoadUserAdmin()
+{
+    document.getElementById('delete').onclick = onClickDelete;
+}
+
+function onClickDelete()
+{
+    var setDisable;
+
+    if(this.checked) {
+        setDisable = true;
+    } else {
+        setDisable = false;
+    }
+
+    var formElements = this.parentNode.parentNode.getElementsByTagName('input');
+    for(i = 0; i < formElements.length; i++) {
+        if(formElements[i].id == 'delete') {
+            continue;
+        }
+        if(formElements[i].type == 'submit') {
+            continue;
+        }
+        formElements[i].disabled = setDisable;
+    }
+    formElements = this.parentNode.parentNode.getElementsByTagName('select');
+    for(i = 0; i < formElements.length; i++) {
+        if(formElements[i].id == 'delete') {
+            continue;
+        }
+        if(formElements[i].type == 'submit') {
+            continue;
+        }
+        formElements[i].disabled = setDisable;
+    }
+    formElements = this.parentNode.parentNode.getElementsByTagName('textarea');
+    for(i = 0; i < formElements.length; i++) {
+        if(formElements[i].id == 'delete') {
+            continue;
+        }
+        if(formElements[i].type == 'submit') {
+            continue;
+        }
+        formElements[i].disabled = setDisable;
+    }
+}
\ No newline at end of file