diff --git a/bootstrap.php b/bootstrap.php
index c7ba0a41575eab6fc47ed3b566359303ab4f1795..6fa05aa508dc48443d1235f8fb3f1a5a09aa755b 100644
--- a/bootstrap.php
+++ b/bootstrap.php
@@ -81,7 +81,7 @@ class Bootstrap {
         define('TEAM_MEMBER_TABLE',$prefix.'team_member');
         define('DEPT_TABLE',$prefix.'department');
         define('GROUP_TABLE', $prefix.'group');
-        define('GROUP_DEPT_TABLE', $prefix.'group_dept_access');
+        define('STAFF_DEPT_TABLE', $prefix.'staff_dept_access');
         define('ROLE_TABLE', $prefix.'role');
 
         define('FAQ_TABLE',$prefix.'faq');
diff --git a/include/ajax.admin.php b/include/ajax.admin.php
index 0cee34e4c6b522e8a07371913c927f108a007128..ca34433480a46eb6091730a9d9068de46d6bdc1f 100644
--- a/include/ajax.admin.php
+++ b/include/ajax.admin.php
@@ -44,7 +44,7 @@ class AdminAjaxAPI extends AjaxController {
         }
 
         $title = __("Add New Department");
-        $path = $ost->get_path_info();
+        $path = ltrim($ost->get_path_info(), '/');
 
         include STAFFINC_DIR . 'templates/quick-add.tmpl.php';
     }
@@ -88,7 +88,7 @@ class AdminAjaxAPI extends AjaxController {
         }
 
         $title = __("Add New Team");
-        $path = $ost->get_path_info();
+        $path = ltrim($ost->get_path_info(), '/');
 
         include STAFFINC_DIR . 'templates/quick-add.tmpl.php';
     }
diff --git a/include/ajax.kbase.php b/include/ajax.kbase.php
index 30575ac22b05ee80bcf3aa07d1e52ed9fe5d3b0a..10266e242d680d7fb231831dab3c95c54ef05f9c 100644
--- a/include/ajax.kbase.php
+++ b/include/ajax.kbase.php
@@ -55,7 +55,7 @@ class KbaseAjaxAPI extends AjaxController {
                 $faq->getId(),
                 $faq->getNumAttachments());
         if($thisstaff
-                && $thisstaff->getRole()->hasPerm(FAQ::PERM_MANAGE)) {
+                && $thisstaff->hasPerm(FAQ::PERM_MANAGE)) {
             $resp.=sprintf(' | <a href="faq.php?id=%d&a=edit">'.__('Edit').'</a>',$faq->getId());
 
         }
diff --git a/include/ajax.orgs.php b/include/ajax.orgs.php
index b6a4002feac751e5a2bbf1a3a6ac34f38ee2db45..c4d73187bf6d2c85c2bf1dc4a7c0f6650174e233 100644
--- a/include/ajax.orgs.php
+++ b/include/ajax.orgs.php
@@ -54,7 +54,7 @@ class OrgsAjaxAPI extends AjaxController {
 
         if(!$thisstaff)
             Http::response(403, 'Login Required');
-        elseif (!$thisstaff->getRole()->hasPerm(Organization::PERM_EDIT))
+        elseif (!$thisstaff->hasPerm(Organization::PERM_EDIT))
             Http::response(403, 'Permission Denied');
         elseif(!($org = Organization::lookup($id)))
             Http::response(404, 'Unknown organization');
@@ -74,7 +74,7 @@ class OrgsAjaxAPI extends AjaxController {
 
         if(!$thisstaff)
             Http::response(403, 'Login Required');
-        elseif (!$thisstaff->getRole()->hasPerm(Organization::PERM_EDIT))
+        elseif (!$thisstaff->hasPerm(Organization::PERM_EDIT))
             Http::response(403, 'Permission Denied');
         elseif(!($org = Organization::lookup($id)))
             Http::response(404, 'Unknown organization');
@@ -101,7 +101,7 @@ class OrgsAjaxAPI extends AjaxController {
 
         if (!$thisstaff)
             Http::response(403, 'Login Required');
-        elseif (!$thisstaff->getRole()->hasPerm(Organization::PERM_DELETE))
+        elseif (!$thisstaff->hasPerm(Organization::PERM_DELETE))
             Http::response(403, 'Permission Denied');
         elseif (!($org = Organization::lookup($id)))
             Http::response(404, 'Unknown organization');
@@ -122,7 +122,7 @@ class OrgsAjaxAPI extends AjaxController {
 
         if (!$thisstaff)
             Http::response(403, 'Login Required');
-        elseif (!$thisstaff->getRole()->hasPerm(User::PERM_EDIT))
+        elseif (!$thisstaff->hasPerm(User::PERM_EDIT))
             Http::response(403, 'Permission Denied');
         elseif (!($org = Organization::lookup($id)))
             Http::response(404, 'Unknown organization');
@@ -145,7 +145,7 @@ class OrgsAjaxAPI extends AjaxController {
                             Format::htmlchars($user->getName()));
             } else { //Creating new  user
                 $form = UserForm::getUserForm()->getForm($_POST);
-                $can_create = $thisstaff->getRole()->hasPerm(User::PERM_CREATE);
+                $can_create = $thisstaff->hasPerm(User::PERM_CREATE);
                 if (!($user = User::fromForm($form, $can_create)))
                     $info['error'] = __('Error adding user - try again!');
             }
@@ -184,7 +184,7 @@ class OrgsAjaxAPI extends AjaxController {
 
         if (!$thisstaff)
             Http::response(403, 'Login Required');
-        elseif (!$thisstaff->getRole()->hasPerm(Organization::PERM_CREATE))
+        elseif (!$thisstaff->hasPerm(Organization::PERM_CREATE))
             Http::response(403, 'Permission Denied');
         elseif (!($org = Organization::lookup($org_id)))
             Http::response(404, 'No such organization');
@@ -211,7 +211,7 @@ class OrgsAjaxAPI extends AjaxController {
     function addOrg() {
         global $thisstaff;
 
-        if (!$thisstaff->getRole()->hasPerm(Organization::PERM_CREATE))
+        if (!$thisstaff->hasPerm(Organization::PERM_CREATE))
             Http::response(403, 'Permission Denied');
 
         $info = array();
@@ -285,7 +285,7 @@ class OrgsAjaxAPI extends AjaxController {
 
         if (!$thisstaff)
             Http::response(403, "Login required");
-        elseif (!$thisstaff->getRole()->hasPerm(Organization::PERM_EDIT))
+        elseif (!$thisstaff->hasPerm(Organization::PERM_EDIT))
             Http::response(403, 'Permission Denied');
         elseif (!($org = Organization::lookup($org_id)))
             Http::response(404, "No such ticket");
diff --git a/include/ajax.users.php b/include/ajax.users.php
index e00e811a93dffae117d092cf037405e1cd1b2d1f..4b3c64fd2ffa67e7b2b6cda925eb1e1b3ff6add9 100644
--- a/include/ajax.users.php
+++ b/include/ajax.users.php
@@ -110,7 +110,7 @@ class UsersAjaxAPI extends AjaxController {
 
         if(!$thisstaff)
             Http::response(403, 'Login Required');
-        elseif (!$thisstaff->getRole()->hasPerm(User::PERM_EDIT))
+        elseif (!$thisstaff->hasPerm(User::PERM_EDIT))
             Http::response(403, 'Permission Denied');
         elseif(!($user = User::lookup($id)))
             Http::response(404, 'Unknown user');
@@ -128,7 +128,7 @@ class UsersAjaxAPI extends AjaxController {
 
         if(!$thisstaff)
             Http::response(403, 'Login Required');
-        elseif (!$thisstaff->getRole()->hasPerm(User::PERM_EDIT))
+        elseif (!$thisstaff->hasPerm(User::PERM_EDIT))
             Http::response(403, 'Permission Denied');
         elseif(!($user = User::lookup($id)))
             Http::response(404, 'Unknown user');
@@ -146,7 +146,7 @@ class UsersAjaxAPI extends AjaxController {
 
         if (!$thisstaff)
             Http::response(403, 'Login Required');
-        elseif (!$thisstaff->getRole()->hasPerm(User::PERM_MANAGE))
+        elseif (!$thisstaff->hasPerm(User::PERM_MANAGE))
             Http::response(403, 'Permission Denied');
         elseif (!($user = User::lookup($id)))
             Http::response(404, 'Unknown user');
@@ -175,7 +175,7 @@ class UsersAjaxAPI extends AjaxController {
 
         if (!$thisstaff)
             Http::response(403, 'Login Required');
-        elseif (!$thisstaff->getRole()->hasPerm(User::PERM_MANAGE))
+        elseif (!$thisstaff->hasPerm(User::PERM_MANAGE))
             Http::response(403, 'Permission Denied');
         elseif (!($user = User::lookup($id)))
             Http::response(404, 'Unknown user');
@@ -209,7 +209,7 @@ class UsersAjaxAPI extends AjaxController {
 
         if (!$thisstaff)
             Http::response(403, 'Login Required');
-        elseif (!$thisstaff->getRole()->hasPerm(User::PERM_DELETE))
+        elseif (!$thisstaff->hasPerm(User::PERM_DELETE))
             Http::response(403, 'Permission Denied');
         elseif (!($user = User::lookup($id)))
             Http::response(404, 'Unknown user');
@@ -257,7 +257,7 @@ class UsersAjaxAPI extends AjaxController {
             $info['lookup'] = 'local';
 
         if ($_POST) {
-            if (!$thisstaff->getRole()->hasPerm(User::PERM_CREATE))
+            if (!$thisstaff->hasPerm(User::PERM_CREATE))
                 Http::response(403, 'Permission Denied');
 
             $info['title'] = __('Add New User');
@@ -276,7 +276,7 @@ class UsersAjaxAPI extends AjaxController {
 
         if (!$thisstaff)
             Http::response(403, 'Login Required');
-        elseif (!$thisstaff->getRole()->hasPerm(User::PERM_CREATE))
+        elseif (!$thisstaff->hasPerm(User::PERM_CREATE))
             Http::response(403, 'Permission Denied');
         elseif (!$bk || !$id)
             Http::response(422, 'Backend and user id required');
@@ -299,7 +299,7 @@ class UsersAjaxAPI extends AjaxController {
 
         if (!$thisstaff)
             Http::response(403, 'Login Required');
-        elseif (!$thisstaff->getRole()->hasPerm(User::PERM_CREATE))
+        elseif (!$thisstaff->hasPerm(User::PERM_CREATE))
             Http::response(403, 'Permission Denied');
 
         $info = array(
@@ -340,7 +340,7 @@ class UsersAjaxAPI extends AjaxController {
         global $thisstaff;
 
         if (!$info or !$info['title']) {
-            if ($thisstaff->getRole()->hasPerm(User::PERM_CREATE))
+            if ($thisstaff->hasPerm(User::PERM_CREATE))
                 $info += array('title' => __('Lookup or create a user'));
             else
                 $info += array('title' => __('Lookup a user'));
@@ -445,7 +445,7 @@ class UsersAjaxAPI extends AjaxController {
 
         if (!$thisstaff)
             Http::response(403, "Login required");
-        elseif (!$thisstaff->getRole()->hasPerm(User::PERM_EDIT))
+        elseif (!$thisstaff->hasPerm(User::PERM_EDIT))
             Http::response(403, 'Permission Denied');
         elseif (!($user = User::lookup($user_id)))
             Http::response(404, "No such user");
diff --git a/include/class.dept.php b/include/class.dept.php
index 473dca73bc5b4389c800510341e62d8daf619fcb..f09ad0587370b9adfac33298c2f9ed2a1b0c7df2 100644
--- a/include/class.dept.php
+++ b/include/class.dept.php
@@ -42,10 +42,10 @@ implements TemplateVariable {
                 'list' => true,
                 'reverse' => 'Staff.dept',
             ),
-            'groups' => array(
+            'extended' => array(
                 'null' => true,
                 'list' => true,
-                'reverse' => 'GroupDeptAccess.dept'
+                'reverse' => 'StaffDeptAccess.dept'
             ),
         ),
     );
@@ -155,15 +155,14 @@ implements TemplateVariable {
                 ->filter(Q::any(array(
                     'dept_id' => $this->getId(),
                     new Q(array(
-                        'group__depts__dept_id' => $this->getId(),
-                        'group__depts__dept__group_membership' => self::ALERTS_DEPT_AND_GROUPS,
+                        'dept_access__dept_id' => $this->getId(),
+                        'dept_access__dept__group_membership' => self::ALERTS_DEPT_AND_GROUPS,
                     )),
                     'staff_id' => $this->manager_id
                 )));
 
             if ($criteria && $criteria['available'])
                 $members->filter(array(
-                    'group__flags__hasbit' => Group::FLAG_ENABLED,
                     'isactive' => 1,
                     'onvacation' => 0,
                 ));
@@ -314,53 +313,7 @@ implements TemplateVariable {
         return $this->getHashtable();
     }
 
-    function getAllowedGroups() {
-
-        if (!isset($this->_groupids)) {
-            $this->_groupids = array();
-            $groups = GroupDeptAccess::objects()
-                ->filter(array('dept_id' => $this->getId()))
-                ->values_flat('group_id');
-
-            foreach ($groups as $row)
-                $this->_groupids[] = $row[0];
-        }
-
-        return $this->_groupids;
-    }
-
-    function updateGroups($groups_ids, $vars) {
-
-        // Groups allowed to access department
-        if (is_array($groups_ids)) {
-            $groups = GroupDeptAccess::objects()
-                ->filter(array('dept_id' => $this->getId()));
-            foreach ($groups as $group) {
-                if ($idx = array_search($group->group_id, $groups_ids)) {
-                    unset($groups_ids[$idx]);
-                    $roleId = $vars['group'.$group->group_id.'_role_id'];
-                    if ($roleId != $group->role_id) {
-                        $group->set('role_id', $roleId ?: 0);
-                        $group->save();
-                    }
-                } else {
-                    $group->delete();
-                }
-            }
-            foreach ($groups_ids as $id) {
-                $roleId = $vars['group'.$id.'_role_id'];
-                GroupDeptAccess::create(array(
-                    'dept_id' => $this->getId(),
-                    'group_id' => $id,
-                    'role_id' => $roleId ?: 0,
-                ))->save();
-            }
-        }
-
-    }
-
     function updateSettings($vars) {
-        $this->updateGroups($vars['groups'] ?: array(), $vars);
         $this->path = $this->getFullPath();
         $this->save();
         return true;
@@ -391,10 +344,11 @@ implements TemplateVariable {
             db_query('UPDATE '.EMAIL_TABLE.' SET dept_id=0 WHERE dept_id='.db_input($id));
             db_query('UPDATE '.FILTER_TABLE.' SET dept_id=0 WHERE dept_id='.db_input($id));
 
-            //Delete group access
-            db_query('DELETE FROM '.GROUP_DEPT_TABLE.' WHERE dept_id='.db_input($id));
+            // Delete extended access
+            StaffDeptAccess::objects()
+                ->filter(array('dept_id' => $id))
+                ->delete();
         }
-
         return true;
     }
 
@@ -607,24 +561,6 @@ implements TemplateVariable {
 
 }
 
-class GroupDeptAccess extends VerySimpleModel {
-    static $meta = array(
-        'table' => GROUP_DEPT_TABLE,
-        'pk' => array('dept_id', 'group_id'),
-        'joins' => array(
-            'dept' => array(
-                'constraint' => array('dept_id' => 'Dept.id'),
-            ),
-            'group' => array(
-                'constraint' => array('group_id' => 'Group.id'),
-            ),
-            'role' => array(
-                'constraint' => array('role_id' => 'Role.id'),
-            ),
-        ),
-    );
-}
-
 class DepartmentQuickAddForm
 extends Form {
     function getFields() {
diff --git a/include/class.export.php b/include/class.export.php
index 68b907282853cbf55b5ebde39f367230905d4ebc..d12dd3bf4762ca9456c56ae31a270bf249264549 100644
--- a/include/class.export.php
+++ b/include/class.export.php
@@ -341,7 +341,7 @@ class DatabaseExporter {
     var $options;
     var $tables = array(CONFIG_TABLE, SYSLOG_TABLE, FILE_TABLE,
         FILE_CHUNK_TABLE, STAFF_TABLE, DEPT_TABLE, TOPIC_TABLE, GROUP_TABLE,
-        GROUP_DEPT_TABLE, TEAM_TABLE, TEAM_MEMBER_TABLE, FAQ_TABLE,
+        STAFF_DEPT_TABLE, TEAM_TABLE, TEAM_MEMBER_TABLE, FAQ_TABLE,
         FAQ_TOPIC_TABLE, FAQ_CATEGORY_TABLE, DRAFT_TABLE,
         CANNED_TABLE, TICKET_TABLE, ATTACHMENT_TABLE,
         THREAD_TABLE, THREAD_ENTRY_TABLE, THREAD_ENTRY_EMAIL_TABLE,
diff --git a/include/class.group.php b/include/class.group.php
deleted file mode 100644
index c8b8e6ef76c36ab51faf0fc1d4e88f75d37f48f3..0000000000000000000000000000000000000000
--- a/include/class.group.php
+++ /dev/null
@@ -1,314 +0,0 @@
-<?php
-/*********************************************************************
-    class.group.php
-
-    User Group - Everything about a group!
-
-    Peter Rotich <peter@osticket.com>
-    Copyright (c)  2006-2013 osTicket
-    http://www.osticket.com
-
-    Released under the GNU General Public License WITHOUT ANY WARRANTY.
-    See LICENSE.TXT for details.
-
-    vim: expandtab sw=4 ts=4 sts=4:
-**********************************************************************/
-
-class Group extends VerySimpleModel {
-
-    static $meta = array(
-        'table' => GROUP_TABLE,
-        'pk' => array('id'),
-        'joins' => array(
-            'members' => array(
-                'null' => true,
-                'list' => true,
-                'reverse' => 'Staff.group',
-            ),
-            'depts' => array(
-                'null' => true,
-                'list' => true,
-                'reverse' => 'GroupDeptAccess.group',
-            ),
-            'role' => array(
-                'constraint' => array('role_id' => 'Role.id')
-            ),
-        ),
-    );
-
-    const FLAG_ENABLED = 0X0001;
-
-    var $departments;
-
-    function getHashtable() {
-        $base = $this->ht;
-        $base['name'] = $base['name'];
-        $base['isactive'] = $base['flags'];
-        return $base;
-    }
-
-    function getInfo() {
-        return $this->getHashtable();
-    }
-
-    function getId() {
-        return $this->id;
-    }
-
-    function getRoleId() {
-        return $this->role_id;
-    }
-
-    function getRole($deptId=0) {
-
-        if ($deptId // Department specific role.
-                && ($roles=$this->getDepartmentsAccess())
-                && isset($roles[$deptId])
-                && $roles[$deptId]
-                && ($role=Role::lookup($roles[$deptId]))
-                && $role->isEnabled())
-            return $role;
-
-        // Default role for this group.
-        return $this->role;
-    }
-
-    function getName() {
-        return $this->name;
-    }
-
-    function getCreateDate() {
-        return $this->created;
-    }
-
-    function getUpdateDate() {
-        return $this->updated;
-    }
-
-    function getNumMembers() {
-        return $this->members ? $this->members->count() : 0;
-     }
-
-    function isEnabled() {
-        return ($this->get('flags') & self::FLAG_ENABLED !== 0);
-    }
-
-    function isActive(){
-        return $this->isEnabled();
-    }
-
-    function getTranslateTag($subtag) {
-        return _H(sprintf('group.%s.%s', $subtag, $this->getId()));
-    }
-    function getLocal($subtag) {
-        $tag = $this->getTranslateTag($subtag);
-        $T = CustomDataTranslation::translate($tag);
-        return $T != $tag ? $T : $this->ht[$subtag];
-    }
-    static function getLocalById($id, $subtag, $default) {
-        $tag = _H(sprintf('group.%s.%s', $subtag, $id));
-        $T = CustomDataTranslation::translate($tag);
-        return $T != $tag ? $T : $default;
-    }
-
-    //Get members of the group.
-    function getMembers() {
-
-        if (!$this->members) {
-            $this->members = Staff::objects()
-                ->filter(array('group_id'=>$this->getId()))
-                ->order_by('lastname', 'firstname')
-                ->all();
-        }
-        return $this->members;
-    }
-
-    //Get departments & roles the group is allowed to access.
-    function getDepartments() {
-        return array_keys($this->getDepartmentsAccess());
-    }
-
-    function getDepartmentsAccess() {
-        if (!isset($this->departments)) {
-            $this->departments = array();
-            foreach (GroupDeptAccess::objects()
-                ->filter(array('group_id'=>$this->getId()))
-                ->values_flat('dept_id', 'role_id') as $gda
-            ) {
-                $this->departments[$gda[0]] = $gda[1];
-            }
-        }
-
-        return $this->departments;
-    }
-
-    function updateDeptAccess($dept_ids, $vars=array()) {
-        if (is_array($dept_ids)) {
-            $groups = GroupDeptAccess::objects()
-                ->filter(array('group_id' => $this->getId()));
-            foreach ($groups as $group) {
-                if ($idx = array_search($group->dept_id, $dept_ids)) {
-                    unset($dept_ids[$idx]);
-                    $roleId = $vars['dept'.$group->dept_id.'_role_id'];
-                    if ($roleId != $group->role_id) {
-                        $group->set('role_id', $roleId ?: 0);
-                        $group->save();
-                    }
-                } else {
-                    $group->delete();
-                }
-            }
-            foreach ($dept_ids as $id) {
-                $roleId = $vars['dept'.$id.'_role_id'];
-                GroupDeptAccess::create(array(
-                    'group_id' => $this->getId(),
-                    'dept_id' => $id,
-                    'role_id' => $roleId ?: 0
-                ))->save();
-            }
-        }
-        return true;
-    }
-
-    function delete() {
-
-        // Can't delete with members
-        if ($this->getNumMembers())
-            return false;
-
-        if (!parent::delete())
-            return false;
-
-        // Remove dept access entries
-        GroupDeptAccess::objects()
-            ->filter(array('group_id'=>$this->getId()))
-            ->delete();
-
-        return true;
-    }
-
-    function __toString() {
-        return $this->getName();
-    }
-
-    function save($refetch=false) {
-        if ($this->dirty) {
-            $this->updated = SqlFunction::NOW();
-        }
-        return parent::save($refetch || $this->dirty);
-    }
-
-    function update($vars,&$errors) {
-        if (isset($this->id) && $this->getId() != $vars['id'])
-            $errors['err'] = __('Missing or invalid group ID');
-
-        if (!$vars['name']) {
-            $errors['name'] = __('Group name required');
-        } elseif(strlen($vars['name'])<3) {
-            $errors['name'] = __('Group name must be at least 3 chars.');
-        } elseif (($gid=static::getIdByName($vars['name']))
-                && (!isset($this->id) || $gid!=$this->getId())) {
-            $errors['name'] = __('Group name already exists');
-        }
-
-        if (!$vars['role_id'])
-            $errors['role_id'] = __('Role selection required');
-
-        if ($errors)
-            return false;
-
-        $this->name = Format::striptags($vars['name']);
-        $this->role_id = $vars['role_id'];
-        $this->notes = Format::sanitize($vars['notes']);
-
-        if ($vars['isactive'])
-            $this->flags = ($this->flags | self::FLAG_ENABLED);
-        else
-            $this->flags =  ($this->flags & ~self::FLAG_ENABLED);
-
-        if ($this->save())
-            return $this->updateDeptAccess($vars['depts'] ?: array(), $vars);
-
-        if (isset($this->id)) {
-            $errors['err']=sprintf(__('Unable to update %s.'), __('this group'))
-               .' '.__('Internal error occurred');
-        }
-        else {
-            $errors['err']=sprintf(__('Unable to create %s.'), __('this group'))
-               .' '.__('Internal error occurred');
-        }
-        return false;
-    }
-
-    /*** Static functions ***/
-    static function getIdByName($name){
-        $id = static::objects()->filter(array('name'=>trim($name)))
-            ->values_flat('id')->first();
-
-        return $id ? $id[0] : 0;
-    }
-
-    static function create($vars=false) {
-
-        if (!isset($vars['flags']))
-            $vars['flags'] = 0;
-
-        $group = parent::create($vars);
-        $group->created = SqlFunction::NOW();
-        return $group;
-    }
-
-    static function __create($vars, &$errors) {
-        $g = self::create($vars);
-        $g->save();
-        if ($vars['depts'])
-            $g->updateDeptAccess($vars['depts'], $vars);
-
-        return $g;
-    }
-
-    static function getGroups($criteria=array()) {
-        static $groups = null;
-        if (!isset($groups) || $criteria) {
-            $groups = array();
-            $query = static::objects()
-                ->values_flat('id', 'name', 'flags')
-                ->order_by('name');
-
-            $filters = array();
-            if (isset($criteria['active']))
-                $filters += array(
-                        'isactive' => $criteria['active'] ? 1 : 0);
-
-            if ($filters)
-                $query->filter($filters);
-
-            $names = array();
-            foreach ($query as $row) {
-                list($id, $name, $flags) = $row;
-                $names[$id] = sprintf('%s%s',
-                    self::getLocalById($id, 'name', $name),
-                    $flags ? '' : ' ' . __('(disabled)'));
-            }
-
-            //TODO: sort if $criteria['localize'];
-            if ($criteria)
-                return $names;
-
-            $groups = $names;
-        }
-
-        return $groups;
-    }
-
-    static function getActiveGroups() {
-        static $groups = null;
-
-        if (!isset($groups))
-            $groups = self::getGroups(array('active'=>true));
-
-        return $groups;
-    }
-
-}
-?>
diff --git a/include/class.nav.php b/include/class.nav.php
index 867f34623c541ac6191cc6c4843221b7a63b7899..94f7a7ca44dad7fdec96723d9ee6b29ee9751545 100644
--- a/include/class.nav.php
+++ b/include/class.nav.php
@@ -118,7 +118,7 @@ class StaffNav {
             $this->tabs['dashboard'] = array(
                 'desc'=>__('Dashboard'),'href'=>'dashboard.php','title'=>__('Agent Dashboard'), "class"=>"no-pjax"
             );
-            if ($thisstaff->getRole()->hasPerm(User::PERM_DIRECTORY)) {
+            if ($thisstaff->hasPerm(User::PERM_DIRECTORY)) {
                 $this->tabs['users'] = array(
                     'desc' => __('Users'), 'href' => 'users.php', 'title' => __('User Directory')
                 );
@@ -175,7 +175,7 @@ class StaffNav {
                 case 'kbase':
                     $subnav[]=array('desc'=>__('FAQs'),'href'=>'kb.php', 'urls'=>array('faq.php'), 'iconclass'=>'kb');
                     if($staff) {
-                        if ($staff->getRole()->hasPerm(FAQ::PERM_MANAGE))
+                        if ($staff->hasPerm(FAQ::PERM_MANAGE))
                             $subnav[]=array('desc'=>__('Categories'),'href'=>'categories.php','iconclass'=>'faq-categories');
                         if ($cfg->isCannedResponseEnabled() && $staff->getRole()->hasPerm(CannedModel::PERM_MANAGE))
                             $subnav[]=array('desc'=>__('Canned Responses'),'href'=>'canned.php','iconclass'=>'canned');
@@ -273,7 +273,6 @@ class AdminNav extends StaffNav{
                 case 'staff':
                     $subnav[]=array('desc'=>__('Agents'),'href'=>'staff.php','iconclass'=>'users');
                     $subnav[]=array('desc'=>__('Teams'),'href'=>'teams.php','iconclass'=>'teams');
-                    $subnav[]=array('desc'=>__('Groups'),'href'=>'groups.php','iconclass'=>'groups');
                     $subnav[]=array('desc'=>__('Roles'),'href'=>'roles.php','iconclass'=>'lists');
                     $subnav[]=array('desc'=>__('Departments'),'href'=>'departments.php','iconclass'=>'departments');
                     break;
diff --git a/include/class.orm.php b/include/class.orm.php
index b0aa78eaac87d714b708be1e2e541cf60f996aef..6fbdf0ae45b12fdc5b7bd4dd0d787f71b3f58893 100644
--- a/include/class.orm.php
+++ b/include/class.orm.php
@@ -308,7 +308,6 @@ class VerySimpleModel {
         if (in_array($field, static::getMeta('fields')))
             return null;
 
-        // TODO: Inspect fields from database before throwing this error
         throw new OrmException(sprintf(__('%s: %s: Field not defined'),
             get_class($this), $field));
     }
@@ -316,6 +315,15 @@ class VerySimpleModel {
         return $this->get($field, null);
     }
 
+    function getByPath($path) {
+        if (is_string($path))
+            $path = explode('__', $path);
+        $root = $this;
+        foreach ($path as $P)
+            $root = $root->get($P);
+        return $root;
+    }
+
     function __isset($field) {
         return array_key_exists($field, $this->ht)
             || isset(static::$meta['joins'][$field]);
@@ -1556,6 +1564,34 @@ class InstrumentedList extends ModelInstanceManager {
         return new static(array($this->model, $key), $this->filter($constraint));
     }
 
+    /**
+     * Find the first item in the current set which matches the given criteria.
+     * This would be used in favor of ::filter() which might trigger another
+     * database query. The criteria is intended to be quite simple and should
+     * not traverse relationships which have not already been fetched.
+     * Otherwise, the ::filter() or ::window() methods would provide better
+     * performance.
+     *
+     * Example:
+     * >>> $a = new User();
+     * >>> $a->roles->add(Role::lookup(['name' => 'administator']));
+     * >>> $a->roles->findFirst(['roles__name__startswith' => 'admin']);
+     * <Role: administrator>
+     */
+    function findFirst(array $criteria) {
+        foreach ($this as $record) {
+            $matches = true;
+            foreach ($criteria as $field=>$check) {
+                if (!SqlCompiler::evaluate($record, $field, $check)) {
+                    $matches = false;
+                    break;
+                }
+            }
+            if ($matches)
+                return $record;
+        }
+    }
+
     // QuerySet delegates
     function count() {
         return $this->objects()->count();
@@ -1614,6 +1650,56 @@ class SqlCompiler {
         return $this->options['parent'];
     }
 
+    /**
+     * Split a criteria item into the identifying pieces: path, field, and
+     * operator.
+     */
+    static function splitCriteria($criteria) {
+        static $operators = array(
+            'exact' => 1, 'isnull' => 1,
+            'gt' => 1, 'lt' => 1, 'gte' => 1, 'lte' => 1,
+            'contains' => 1, 'like' => 1, 'startswith' => 1, 'endswith' => 1,
+            'in' => 1, 'intersect' => 1,
+            'hasbit' => 1,
+        );
+        $path = explode('__', $criteria);
+        if (!isset($options['table'])) {
+            $field = array_pop($path);
+            if (isset($operators[$field])) {
+                $operator = $field;
+                $field = array_pop($path);
+            }
+        }
+        return array($field, $path, $operator ?: 'exact');
+    }
+
+    /**
+     * Check if the values match given the operator.
+     *
+     * Throws:
+     * OrmException - if $operator is not supported
+     */
+    static function evaluate($record, $field, $check) {
+        static $ops; if (!isset($ops)) { $ops = array(
+            'exact' => function($a, $b) { return is_string($a) ? strcasecmp($a, $b) == 0 : $a == $b; },
+            'isnull' => function($a, $b) { return is_null($a) == $b; },
+            'gt' => function($a, $b) { return $a > $b; },
+            'gte' => function($a, $b) { return $a >= $b; },
+            'lt' => function($a, $b) { return $a < $b; },
+            'lte' => function($a, $b) { return $a <= $b; },
+            'contains' => function($a, $b) { return stripos($a, $b) !== false; },
+            'startswith' => function($a, $b) { return stripos($a, $b) === 0; },
+            'hasbit' => function($a, $b) { return $a & $b == $b; },
+        ); }
+        list($field, $path, $operator) = self::splitCriteria($field);
+        if (!isset($ops[$operator]))
+            throw new OrmException($operator.': Unsupported operator');
+
+        if ($path)
+            $record = $record->getByPath($path);
+        return $ops[$operator]($record->get($field), $check);
+    }
+
     /**
      * Handles breaking down a field or model search descriptor into the
      * model search path, field, and operator parts. When used in a queryset
@@ -1680,16 +1766,8 @@ class SqlCompiler {
         // The parts after each of the __ pieces are links to other tables.
         // The last item (after the last __) is allowed to be an operator
         // specifiction.
-        $parts = explode('__', $field);
-        $operator = static::$operators['exact'];
-        if (!isset($options['table'])) {
-            $field = array_pop($parts);
-            if (isset(static::$operators[$field])) {
-                $operator = static::$operators[$field];
-                $field = array_pop($parts);
-            }
-        }
-
+        list($field, $parts, $op) = static::splitCriteria($field);
+        $operator = static::$operators[$op];
         $path = '';
         $rootModel = $model;
 
diff --git a/include/class.role.php b/include/class.role.php
index ce4256c5ba75da5a0c67eac8b51319a040758562..5f73d8d5684b23584d7d2a949424a3cc049b1191 100644
--- a/include/class.role.php
+++ b/include/class.role.php
@@ -19,10 +19,10 @@ class RoleModel extends VerySimpleModel {
         'table' => ROLE_TABLE,
         'pk' => array('id'),
         'joins' => array(
-            'groups' => array(
+            'extensions' => array(
                 'null' => true,
                 'list' => true,
-                'reverse' => 'Group.role',
+                'reverse' => 'StaffDeptAccess.role',
             ),
             'agents' => array(
                 'reverse' => 'Staff.role',
@@ -70,7 +70,7 @@ class RoleModel extends VerySimpleModel {
     }
 
     function isDeleteable() {
-        return $this->groups->count() + $this->agents->count() == 0;
+        return $this->extensions->count() + $this->agents->count() == 0;
     }
 
 }
diff --git a/include/class.staff.php b/include/class.staff.php
index be5ea3562a664d6693cdc9983a2f4e87de8e67e4..5d686dc113914a35f02d45de77728a38c4677450 100644
--- a/include/class.staff.php
+++ b/include/class.staff.php
@@ -18,7 +18,6 @@ include_once(INCLUDE_DIR.'class.dept.php');
 include_once(INCLUDE_DIR.'class.error.php');
 include_once(INCLUDE_DIR.'class.team.php');
 include_once(INCLUDE_DIR.'class.role.php');
-include_once(INCLUDE_DIR.'class.group.php');
 include_once(INCLUDE_DIR.'class.passwd.php');
 include_once(INCLUDE_DIR.'class.user.php');
 include_once(INCLUDE_DIR.'class.auth.php');
@@ -29,6 +28,7 @@ implements AuthenticatedUser, EmailContact, TemplateVariable {
     static $meta = array(
         'table' => STAFF_TABLE,
         'pk' => array('staff_id'),
+        'select_related' => array('dept'),
         'joins' => array(
             'dept' => array(
                 'constraint' => array('dept_id' => 'Dept.id'),
@@ -36,8 +36,8 @@ implements AuthenticatedUser, EmailContact, TemplateVariable {
             'role' => array(
                 'constraint' => array('role_id' => 'Role.id'),
             ),
-            'group' => array(
-                'constraint' => array('group_id' => 'Group.id'),
+            'dept_access' => array(
+                'reverse' => 'StaffDeptAccess.staff',
             ),
             'teams' => array(
                 'reverse' => 'TeamMember.staff',
@@ -52,7 +52,7 @@ implements AuthenticatedUser, EmailContact, TemplateVariable {
     var $passwd_change;
     var $_roles = null;
     var $_teams = null;
-    var $_perms = null;
+    var $_perm;
 
     function __onload() {
 
@@ -101,8 +101,8 @@ implements AuthenticatedUser, EmailContact, TemplateVariable {
 
     function getHashtable() {
         $base = $this->ht;
-        $base['group'] = $base['group_id'];
         unset($base['teams']);
+        unset($base['dept_access']);
         return $base;
     }
 
@@ -311,7 +311,7 @@ implements AuthenticatedUser, EmailContact, TemplateVariable {
             // Departments the staff is "allowed" to access...
             // based on the group they belong to + user's primary dept + user's managed depts.
             $sql='SELECT DISTINCT d.id FROM '.STAFF_TABLE.' s '
-                .' LEFT JOIN '.GROUP_DEPT_TABLE.' g ON (s.group_id=g.group_id) '
+                .' LEFT JOIN '.STAFF_DEPT_TABLE.' g ON (s.staff_id=g.staff_id) '
                 .' INNER JOIN '.DEPT_TABLE.' d ON (LOCATE(CONCAT("/", s.dept_id, "/"), d.path) OR d.manager_id=s.staff_id OR LOCATE(CONCAT("/", g.dept_id, "/"), d.path)) '
                 .' WHERE s.staff_id='.db_input($this->getId());
             $depts = array();
@@ -325,8 +325,8 @@ implements AuthenticatedUser, EmailContact, TemplateVariable {
                 'path__contains' => '/'.$this->dept_id.'/',
                 'manager_id' => $this->getId(),
             ));
-            // Add in group access
-            foreach ($this->group->depts->values_flat('dept_id') as $row) {
+            // Add in extended access
+            foreach ($this->dept_access->depts->values_flat('dept_id') as $row) {
                 // Skip primary dept
                 if ($row[0] == $this->dept_id)
                     continue;
@@ -342,10 +342,6 @@ implements AuthenticatedUser, EmailContact, TemplateVariable {
                 $depts[] = $row[0];
             */
 
-            if (!$depts) { //Neptune help us! (fallback)
-                $depts = array_merge($this->getGroup()->getDepartments(), array($this->getDeptId()));
-            }
-
             $this->departments = $depts;
         }
 
@@ -363,14 +359,6 @@ implements AuthenticatedUser, EmailContact, TemplateVariable {
                     ))?array_keys($depts):array();
     }
 
-    function getGroupId() {
-        return $this->group_id;
-    }
-
-    function getGroup() {
-        return $this->group;
-    }
-
     function getDeptId() {
         return $this->dept_id;
     }
@@ -400,25 +388,15 @@ implements AuthenticatedUser, EmailContact, TemplateVariable {
             if (isset($this->_roles[$deptId]))
                 return $this->_roles[$deptId];
 
-            if (($role = $this->group->getRole($deptId)))
-                return $this->_roles[$deptId] = $role;
+            if ($access = $this->dept_access->findFirst(array('dept_id' => $deptId)))
+                return $this->_roles[$deptId] = $access->role;
         }
         // For the primary department, use the primary role
         return $this->role;
     }
 
     function hasPerm($perm) {
-        if (!isset($this->_perms)) {
-            $this->_perms = array();
-            foreach ($this->getDepartments() as $deptId) {
-                if (($role = $this->getRole($deptId))) {
-                    foreach ($role->getPermission()->getInfo() as $perm=>$v) {
-                        $this->_perms[$perm] |= $v;
-                    }
-                }
-            }
-        }
-        return @$this->_perms[$perm] ?: false;
+        return $this->getPermission()->has($perm);
     }
 
     function canManageTickets() {
@@ -435,10 +413,6 @@ implements AuthenticatedUser, EmailContact, TemplateVariable {
         return TRUE;
     }
 
-    function isGroupActive() {
-        return $this->group->isEnabled();
-    }
-
     function isactive() {
         return $this->isactive;
     }
@@ -452,7 +426,7 @@ implements AuthenticatedUser, EmailContact, TemplateVariable {
     }
 
     function isAvailable() {
-        return ($this->isactive() && $this->isGroupActive() && !$this->onVacation());
+        return ($this->isactive() && !$this->onVacation());
     }
 
     function showAssignedOnly() {
@@ -545,6 +519,17 @@ implements AuthenticatedUser, EmailContact, TemplateVariable {
         }
     }
 
+    function getPermission() {
+        if (!isset($this->_perm)) {
+            $this->_perm = new RolePermission($this->permissions);
+        }
+        return $this->_perm;
+    }
+
+    function getPermissionInfo() {
+        return $this->getPermission()->getInfo();
+    }
+
     function onLogin($bk) {
         // Update last apparent language preference
         $this->setExtraAttr('browser_lang',
@@ -648,31 +633,29 @@ implements AuthenticatedUser, EmailContact, TemplateVariable {
         return $this->save();
     }
 
-    function updateTeams($team_ids) {
+    function updateTeams($membership, &$errors) {
+        $dropped = array();
+        foreach ($this->teams as $TM)
+            $dropped[$TM->team_id] = 1;
 
-        if (is_array($team_ids)) {
-            $members = TeamMember::objects()
-                ->filter(array('staff_id' => $this->getId()));
-            foreach ($members as $member) {
-                if ($idx = array_search($member->team_id, $team_ids)) {
-                    unset($team_ids[$idx]);
-                } else {
-                    $member->delete();
-                }
+        reset($membership);
+        while(list(, list($team_id, $alerts)) = each($membership)) {
+            $member = $this->teams->findFirst(array('team_id' => $team_id));
+            if (!$member) {
+                $this->teams->add($member = TeamMember::create(array(
+                    'team_id' => $team_id,
+                )));
             }
-
-            foreach ($team_ids as $id) {
-                TeamMember::create(array(
-                    'staff_id'=>$this->getId(),
-                    'team_id'=>$id
-                ))->save();
-            }
-        } else {
-            TeamMember::objects()
-                ->filter(array('staff_id'=>$this->getId()))
+            $member->setAlerts($alerts);
+            if (!$errors)
+                $member->save();
+            unset($dropped[$member->team_id]);
+        }
+        if (!$errors && $dropped) {
+            $member = $this->teams
+                ->filter(array('team_id__in' => array_keys($dropped)))
                 ->delete();
         }
-
         return true;
     }
 
@@ -685,22 +668,24 @@ implements AuthenticatedUser, EmailContact, TemplateVariable {
         if (!parent::delete())
             return false;
 
-        //Update the poster and clear staff_id on ticket thread table.
-        db_query('UPDATE '.THREAD_ENTRY_TABLE
-                .' SET staff_id=0, poster= '.db_input($this->getName()->getOriginal())
-                .' WHERE staff_id='.db_input($this->getId()));
-
         // DO SOME HOUSE CLEANING
         //Move remove any ticket assignments...TODO: send alert to Dept. manager?
-        db_query('UPDATE '.TICKET_TABLE.' SET staff_id=0 WHERE staff_id='.db_input($this->getId()));
+        Ticket::objects()
+            ->filter(array('staff_id' => $this->getId()))
+            ->update(array('staff_id' => 0));
 
         //Update the poster and clear staff_id on ticket thread table.
-        db_query('UPDATE '.TICKET_THREAD_TABLE
-                .' SET staff_id=0, poster= '.db_input($this->getName()->getOriginal())
-                .' WHERE staff_id='.db_input($this->getId()));
+        ThreadEntry::objects()
+            ->filter(array('staff_id' => $this->getId()))
+            ->update(array(
+                'staff_id' => 0,
+                'poster' => $this->getName()->getOriginal(),
+            ));
 
         // Cleanup Team membership table.
-        $this->updateTeams(array());
+        TeamMember::objects()
+            ->filter(array('staff_id'=>$this->getId()))
+            ->delete();
 
         return true;
     }
@@ -726,7 +711,6 @@ implements AuthenticatedUser, EmailContact, TemplateVariable {
 
         if (isset($criteria['available'])) {
             $members = $members->filter(array(
-                'group__flags__hasbit' => Group::FLAG_ENABLED,
                 'onvacation' => 0,
                 'isactive' => 1,
             ));
@@ -896,11 +880,8 @@ implements AuthenticatedUser, EmailContact, TemplateVariable {
         if(!$vars['role_id'])
             $errors['role_id']=__('Role for primary department is required');
 
-        if(!$vars['group_id'])
-            $errors['group_id']=__('Group is required');
-
         // Ensure we will still have an administrator with access
-        if ($vars['isadmin'] !== '1' || $vars['isactive'] !== '1') {
+        if ($vars['isadmin'] !== '1' || $vars['islocked'] === '1') {
             $sql = 'select count(*), max(staff_id) from '.STAFF_TABLE
                 .' WHERE isadmin=1 and isactive=1';
             if (($res = db_query($sql))
@@ -913,17 +894,55 @@ implements AuthenticatedUser, EmailContact, TemplateVariable {
             }
         }
 
-        if ($errors)
-            return false;
+        // Update the user's password if requested
+        if ($vars['passwd1']) {
+            try {
+                $this->setPassword($vars['passwd1'], null);
+            }
+            catch (BadPassword $ex) {
+                $errors['passwd1'] = $ex->getMessage();
+            }
+            catch (PasswordUpdateFailed $ex) {
+                // TODO: Add a warning banner or crash the update
+            }
+            if (isset($vars['change_passwd']))
+                $this->change_passwd = 1;
+        }
+        elseif (!isset($vars['change_passwd'])) {
+            $this->change_passwd = 0;
+        }
+
+        // Update some things for ::updateAccess to inspect
+        $this->dept_id = $vars['dept_id'];
+
+        // Format access update as [array(dept_id, role_id, alerts?)]
+        $access = array();
+        if (isset($vars['dept_access'])) {
+            foreach (@$vars['dept_access'] as $dept_id) {
+                $access[] = array($dept_id, $vars['dept_access_role'][$dept_id],
+                    @$vars['dept_access_alerts'][$dept_id]);
+            }
+        }
+        $this->updateAccess($access, $errors);
+
+        // Format team membership as [array(team_id, alerts?)]
+        $teams = array();
+        if (isset($vars['teams'])) {
+            foreach (@$vars['teams'] as $team_id) {
+                $teams[] = array($team_id, @$vars['team_alerts'][$team_id]);
+            }
+        }
+        $this->updateTeams($teams, $errors);
+
+        // Update the local permissions
+        $this->updatePerms($vars['perms'], $errors);
 
         $this->isadmin = $vars['isadmin'];
-        $this->isactive = $vars['isactive'];
+        $this->isactive = isset($vars['islocked']) ? 0 : 1;
         $this->isvisible = isset($vars['isvisible'])?1:0;
         $this->onvacation = isset($vars['onvacation'])?1:0;
         $this->assigned_only = isset($vars['assigned_only'])?1:0;
-        $this->dept_id = $vars['dept_id'];
         $this->role_id = $vars['role_id'];
-        $this->group_id = $vars['group_id'];
         $this->timezone = $vars['timezone'];
         $this->username = $vars['username'];
         $this->firstname = $vars['firstname'];
@@ -936,25 +955,10 @@ implements AuthenticatedUser, EmailContact, TemplateVariable {
         $this->signature = Format::sanitize($vars['signature']);
         $this->notes = Format::sanitize($vars['notes']);
 
-        // Update the user's password if requested
-        if ($vars['passwd1']) {
-            try {
-                $this->setPassword($vars['passwd1'], null);
-            }
-            catch (BadPassword $ex) {
-                $errors['passwd1'] = $ex->getMessage();
-            }
-            catch (PasswordUpdateFailed $ex) {
-                // TODO: Add a warning banner or crash the update
-            }
-            if (isset($vars['change_passwd']))
-                $this->change_passwd = 1;
-        }
-        elseif (!isset($vars['change_passwd'])) {
-            $this->change_passwd = 0;
-        }
+        if ($errors)
+            return false;
 
-        if ($this->save() && $this->updateTeams($vars['teams'])) {
+        if ($this->save()) {
             if ($vars['welcome_email'])
                 $this->sendResetEmail('registration-staff', false);
             return true;
@@ -969,9 +973,98 @@ implements AuthenticatedUser, EmailContact, TemplateVariable {
         }
         return false;
     }
+
+    /**
+     * Parameters:
+     * $access - (<array($dept_id, $role_id, $alerts)>) a list of the complete,
+     *      extended access for this agent. Any the agent currently has, which
+     *      is not listed will be removed.
+     * $errors - (<array>) list of error messages from the process, which will
+     *      be indexed by the dept_id number.
+     */
+    function updateAccess($access, &$errors) {
+        reset($access);
+        $dropped = array();
+        foreach ($this->dept_access as $DA)
+            $dropped[$DA->dept_id] = 1;
+        while (list(, list($dept_id, $role_id, $alerts)) = each($access)) {
+            unset($dropped[$dept_id]);
+            if (!$role_id || !Role::lookup($role_id))
+                $errors['dept_access'][$dept_id] = __('Select a valid role');
+            if (!$dept_id || !Dept::lookup($dept_id))
+                $errors['dept_access'][$dept_id] = __('Select a valid departent');
+            if ($dept_id == $this->getDeptId())
+                $errors['dept_access'][$dept_id] = __('Agent already has access to this department');
+            $da = $this->dept_access->findFirst(array('dept_id' => $dept_id));
+            if (!isset($da)) {
+                $da = StaffDeptAccess::create(array(
+                    'dept_id' => $dept_id, 'role_id' => $role_id
+                ));
+                $this->dept_access->add($da);
+            }
+            else {
+                $da->role_id = $role_id;
+            }
+            $da->setAlerts($alerts);
+            if (!$errors)
+                $da->save();
+        }
+        if (!$errors && $dropped)
+            $this->dept_access
+                ->filter(array('dept_id__in' => array_keys($dropped)))
+                ->delete();
+        return !$errors;
+    }
+
+    private function updatePerms($vars, &$errors) {
+        $permissions = $this->getPermission();
+        foreach (RolePermission::allPermissions() as $g => $perms) {
+            foreach($perms as $k => $v) {
+                $permissions->set($k, in_array($k, $vars) ? 1 : 0);
+            }
+        }
+        $this->permissions = $permissions->toJson();
+    }
+
 }
 
 interface RestrictedAccess {
     function checkStaffPerm($staff);
 }
+
+class StaffDeptAccess extends VerySimpleModel {
+    static $meta = array(
+        'table' => STAFF_DEPT_TABLE,
+        'pk' => array('staff_id', 'dept_id'),
+        'select_related' => array('dept', 'role'),
+        'joins' => array(
+            'dept' => array(
+                'constraint' => array('dept_id' => 'Dept.id'),
+            ),
+            'staff' => array(
+                'constraint' => array('staff_id' => 'Staff.staff_id'),
+            ),
+            'role' => array(
+                'constraint' => array('role_id' => 'Role.id'),
+            ),
+        ),
+    );
+
+    const FLAG_ALERTS =     0x0001;
+
+    function isAlertsEnabled() {
+        return $this->flags & self::FLAG_ALERTS != 0;
+    }
+
+    function setFlag($flag, $value) {
+        if ($value)
+            $this->flags |= $flag;
+        else
+            $this->flags &= ~$flag;
+    }
+
+    function setAlerts($value) {
+        $this->setFlag(self::FLAG_ALERTS, $value);
+    }
+}
 ?>
diff --git a/include/class.team.php b/include/class.team.php
index 1e397d684263d6a1c32e7cb6b86bf4b11b0d2dee..0d5e7013f2864eccd13d8fcec7450d3e09d0cd66 100644
--- a/include/class.team.php
+++ b/include/class.team.php
@@ -238,7 +238,6 @@ implements TemplateVariable {
                     'flags__hasbit'=>self::FLAG_ENABLED,
                     'members__staff__isactive'=>1,
                     'members__staff__onvacation'=>0,
-                    'members__staff__group__flags__hasbit'=>Group::FLAG_ENABLED,
                 ))
                 ->filter(array('members_count__gt'=>0));
             }
@@ -298,6 +297,23 @@ class TeamMember extends VerySimpleModel {
             ),
         ),
     );
+
+    const FLAG_ALERTS = 0x0001;
+
+    function isAlertsEnabled() {
+        return $this->flags & self::FLAG_ALERTS != 0;
+    }
+
+    function setFlag($flag, $value) {
+        if ($value)
+            $this->flags |= $flag;
+        else
+            $this->flags &= ~$flag;
+    }
+
+    function setAlerts($value) {
+        $this->setFlag(self::FLAG_ALERTS, $value);
+    }
 }
 
 class TeamQuickAddForm
@@ -314,9 +330,10 @@ extends AbstractForm {
                 ),
             )),
             'lead_id' => new ChoiceField(array(
+                'label' => __('Optionally select a leader for the team'),
                 'default' => 0,
                 'choices' => array_merge(
-                    array(0 => '— '.__('No Leader').' —'),
+                    array(0 => '— '.__('None').' —'),
                     Staff::getStaffMembers()
                 ),
                 'configuration' => array(
diff --git a/include/class.ticket.php b/include/class.ticket.php
index 9eb714dc45235412d33b1a2ccbd3a07e203077ea..bcacc0dd3c7363b12ce202acf9caee4df18fe460 100644
--- a/include/class.ticket.php
+++ b/include/class.ticket.php
@@ -2951,7 +2951,7 @@ implements RestrictedAccess, Threadable {
                 }
 
                 $user_form = UserForm::getUserForm()->getForm($vars);
-                $can_create = !$thisstaff || $thisstaff->getRole()->hasPerm(User::PERM_CREATE);
+                $can_create = !$thisstaff || $thisstaff->hasPerm(User::PERM_CREATE);
                 if (!$user_form->isValid($field_filter('user'))
                     || !($user=User::fromVars($user_form->getClean(), $can_create))
                 ) {
diff --git a/include/staff/category.inc.php b/include/staff/category.inc.php
index a506b90ab1bde45edd2015cee18d5e5112e0fc9a..c1b4dcda8f8534268408a8a04958bbb21b1e4a6d 100644
--- a/include/staff/category.inc.php
+++ b/include/staff/category.inc.php
@@ -1,6 +1,6 @@
 <?php
 if (!defined('OSTSCPINC') || !$thisstaff
-        || !$thisstaff->getRole()->hasPerm(FAQ::PERM_MANAGE))
+        || !$thisstaff->hasPerm(FAQ::PERM_MANAGE))
     die('Access Denied');
 
 $info=array();
diff --git a/include/staff/department.inc.php b/include/staff/department.inc.php
index 82653e9b4e54ef7fd8d9147d661e8745787c2b7c..c5ea4cda2e884ce36a971fa43e414f2fb558c6e8 100644
--- a/include/staff/department.inc.php
+++ b/include/staff/department.inc.php
@@ -8,7 +8,6 @@ if($dept && $_REQUEST['a']!='add') {
     $submit_text=__('Save Changes');
     $info = $dept->getInfo();
     $info['id'] = $dept->getId();
-    $info['groups'] = $dept->getAllowedGroups();
     $qs += array('id' => $dept->getId());
 } else {
     $title=__('Add New Department');
@@ -34,8 +33,6 @@ $info = Format::htmlchars(($errors && $_POST) ? $_POST : $info);
 <ul class="clean tabs">
     <li class="active"><a href="#settings">
         <i class="icon-file"></i> <?php echo __('Settings'); ?></a></li>
-    <li><a href="#access">
-        <i class="icon-lock"></i> <?php echo __('Access'); ?></a></li>
 </ul>
 <div id="settings" class="tab_content">
  <table class="form_table" width="940" border="0" cellspacing="0" cellpadding="2">
@@ -290,87 +287,6 @@ $info = Format::htmlchars(($errors && $_POST) ? $_POST : $info);
     </tbody>
 </table>
 </div>
-<div id="access" class="tab_content" style="display:none">
-   <table class="form_table" width="940" border="0" cellspacing="0" cellpadding="2">
-    <thead>
-        <tr>
-            <th colspan=2>
-                <em><?php echo __('Primary department members have access to this department by default'); ?></em>
-            </th>
-        </tr>
-        <tr>
-            <th width="40%"><?php echo __('Group'); ?></th>
-            <th><?php echo __('Role'); ?></th>
-        </tr>
-    </thead>
-    <tbody>
-        <?php
-        $deptId = $dept ? $dept->getId() : 0;
-        $roles = Role::getRoles();
-        $groups = Group::objects()
-            ->annotate(array(
-                'isenabled'=>new SqlExpr(array(
-                        'flags__hasbit' => Group::FLAG_ENABLED))
-                ))
-            ->order_by('name');
-        foreach ($groups as $group) {
-            $DeptAccess = $group->getDepartmentsAccess();
-            ?>
-         <tr>
-            <td>
-             &nbsp;
-             <label>
-              <?php
-              $ck = ($info['groups'] && in_array($group->getId(), $info['groups'])) ? 'checked="checked"' : '';
-              echo sprintf('%s&nbsp;&nbsp;%s',
-                        sprintf('<input type="checkbox" class="grp-ckb"
-                            name="groups[]" value="%s" %s />',
-                            $group->getId(), $ck),
-                        Format::htmlchars($group->getName()));
-              ?>
-             </label>
-            </td>
-            <td>
-                <?php
-                $_name = 'group'.$group->getId().'_role_id';
-                ?>
-                <select name="<?php echo $_name; ?>">
-                    <option value="0">&mdash; <?php
-                        echo sprintf('%s (%s)',
-                                __('Group Default'),
-                                $group->getRole());
-                        ?>
-                        &mdash;</option>
-                    <?php
-                    foreach ($roles as $rid => $role) {
-                        $sel = '';
-                        if (isset($info[$_name]))
-                            $sel = ($info[$_name] == $rid) ? 'selected="selected"' : '';
-                        elseif ($deptId && isset($DeptAccess[$deptId]))
-                            $sel = ($DeptAccess[$deptId] == $rid) ?  'selected="selected"' : '';
-
-                        echo sprintf('<option value="%d" %s>%s</option>',
-                                $rid, $sel, $role);
-                    } ?>
-                </select>
-                <i class="help-tip icon-question-sign" href="#dept-role"></i>
-            </td>
-         </tr>
-         <?php
-        } ?>
-    </tbody>
-    <tfoot>
-     <tr>
-        <td colspan="2">
-            <?php echo __('Select');?>:&nbsp;
-            <a id="selectAll" href="#grp-ckb"><?php echo __('All');?></a>&nbsp;&nbsp;
-            <a id="selectNone" href="#grp-ckb"><?php echo __('None');?></a>&nbsp;&nbsp;
-            <a id="selectToggle" href="#grp-ckb"><?php echo __('Toggle');?></a>&nbsp;&nbsp;
-        </td>
-     </tr>
-    </tfoot>
-   </table>
-</div>
 <p style="text-align:center">
     <input type="submit" name="submit" value="<?php echo $submit_text; ?>">
     <input type="reset"  name="reset"  value="<?php echo __('Reset');?>">
diff --git a/include/staff/faq-category.inc.php b/include/staff/faq-category.inc.php
index b6287bcb9eb3779a9aab6aa5a49dc244683e1856..9f2ff86dd3f548ab34a9064e18fabc8be2ab750c 100644
--- a/include/staff/faq-category.inc.php
+++ b/include/staff/faq-category.inc.php
@@ -17,7 +17,7 @@ if(!defined('OSTSTAFFINC') || !$category || !$thisstaff) die('Access Denied');
 <?php echo Format::display($category->getDescription()); ?>
 </div>
 <?php
-if ($thisstaff->getRole()->hasPerm(FAQ::PERM_MANAGE)) {
+if ($thisstaff->hasPerm(FAQ::PERM_MANAGE)) {
     echo sprintf('<div class="cat-manage-bar"><a href="categories.php?id=%d" class="Icon editCategory">'.__('Edit Category').'</a>
              <a href="categories.php" class="Icon deleteCategory">'.__('Delete Category').'</a>
              <a href="faq.php?cid=%d&a=add" class="Icon newFAQ">'.__('Add New FAQ').'</a></div>',
diff --git a/include/staff/faq-view.inc.php b/include/staff/faq-view.inc.php
index 05a6b0f48cf50d15087677dcb286707f0003c944..d3a4402fd8b607ace6fcc35c047937647f1b49e1 100644
--- a/include/staff/faq-view.inc.php
+++ b/include/staff/faq-view.inc.php
@@ -78,7 +78,7 @@ $query = http_build_query($query); ?>
         <?php echo __('Print'); ?>
     </a>
 <?php
-if ($thisstaff->getRole()->hasPerm(FAQ::PERM_MANAGE)) { ?>
+if ($thisstaff->hasPerm(FAQ::PERM_MANAGE)) { ?>
     <a href="faq.php?id=<?php echo $faq->getId(); ?>&a=edit" class="action-button">
     <i class="icon-edit"></i>
         <?php echo __('Edit FAQ'); ?>
@@ -102,7 +102,7 @@ if ($thisstaff->getRole()->hasPerm(FAQ::PERM_MANAGE)) { ?>
 <hr>
 
 <?php
-if ($thisstaff->getRole()->hasPerm(FAQ::PERM_MANAGE)) { ?>
+if ($thisstaff->hasPerm(FAQ::PERM_MANAGE)) { ?>
 <form action="faq.php?id=<?php echo  $faq->getId(); ?>" method="post">
     <?php csrf_token(); ?>
     <input type="hidden" name="do" value="manage-faq">
diff --git a/include/staff/faq.inc.php b/include/staff/faq.inc.php
index 89584be75d0c313e9e74bc7811076bd000e5e764..fa2f9316bf6e3fb5586b1663e66d8ebe536ad3ac 100644
--- a/include/staff/faq.inc.php
+++ b/include/staff/faq.inc.php
@@ -1,6 +1,6 @@
 <?php
 if (!defined('OSTSCPINC') || !$thisstaff
-        || !$thisstaff->getRole()->hasPerm(FAQ::PERM_MANAGE))
+        || !$thisstaff->hasPerm(FAQ::PERM_MANAGE))
     die('Access Denied');
 
 $info = $qs = array();
diff --git a/include/staff/group.inc.php b/include/staff/group.inc.php
deleted file mode 100644
index 032d0b3f05d5652ae8370449cb7a21373a81f9f6..0000000000000000000000000000000000000000
--- a/include/staff/group.inc.php
+++ /dev/null
@@ -1,184 +0,0 @@
-<?php
-
-if(!defined('OSTADMININC') || !$thisstaff || !$thisstaff->isAdmin()) die('Access Denied');
-$info = $qs = array();
-if ($group) {
-    $title = __('Update Group');
-    $action = 'update';
-    $submit_text = __('Save Changes');
-    $info = $group->getInfo();
-    $info['id'] = $group->getId();
-    $info['depts'] = $group->getDepartments();
-    $trans['name'] = $group->getTranslateTag('name');
-    $qs += array('id' => $group->getId());
-} else {
-    $title = __('Add New Group');
-    $action = 'add';
-    $submit_text = __('Create Group');
-    $info['isactive'] = isset($info['isactive']) ? $info['isactive'] : 1;
-    $qs += array('a' => $_REQUEST['a']);
-}
-
-$info = Format::htmlchars(($errors && $_POST) ? array_merge($info, $_POST) : $info);
-$roles = Role::getActiveRoles();
-
-?>
-<form action="groups.php?<?php echo Http::build_query($qs); ?>" method="post" id="save" name="group">
-    <?php csrf_token(); ?>
-    <input type="hidden" name="do" value="<?php echo $action; ?>">
-    <input type="hidden" name="a" value="<?php echo Format::htmlchars($_REQUEST['a']); ?>">
-    <input type="hidden" name="id" value="<?php echo $info['id']; ?>">
-<h2> <?php echo $group ?: __('New Group'); ?></h2>
-<ul class="clean tabs">
-    <li class="active"><a href="#group">
-        <i class="icon-file"></i> <?php echo __('Group'); ?></a></li>
-    <li><a href="#departments">
-        <i class="icon-lock"></i> <?php echo __('Departments Access'); ?></a></li>
-</ul>
-<div id="group" class="tab_content">
-    <table class="form_table" width="940" border="0" cellspacing="0" cellpadding="2">
-    <thead>
-        <tr>
-            <th colspan="2">
-                <h4><?php echo $title; ?></h4>
-                <em><?php echo __(
-                'Roles are used to define agents\' permissions'
-                ); ?>&nbsp;<i class="help-tip icon-question-sign"
-                href="#roles"></i></em>
-            </th>
-        </tr>
-    </thead>
-    <tbody>
-        <tr>
-            <td width="180" class="required"><?php echo __('Name'); ?>:</td>
-            <td>
-                <input size="50" type="text" name="name" value="<?php echo $info['name']; ?>"
-                autofocus data-translate-tag="<?php echo $trans['name']; ?>"/>
-                <span class="error">*&nbsp;<?php echo $errors['name']; ?></span>
-            </td>
-        </tr>
-        <tr>
-            <td width="180" class="required">
-                <?php echo __('Status');?>:
-            </td>
-            <td>
-                <input type="radio" name="isactive" value="1" <?php
-                    echo $info['isactive'] ? 'checked="checked"' : ''; ?>><strong><?php echo __('Active');?></strong>
-                &nbsp;
-                <input type="radio" name="isactive" value="0" <?php
-                    echo !$info['isactive'] ? 'checked="checked"' : ''; ?>><strong><?php echo __('Disabled');?></strong>
-                &nbsp;<span class="error">*&nbsp;<?php echo $errors['status']; ?></span>
-                <i class="help-tip icon-question-sign" href="#status"></i>
-            </td>
-        </tr>
-        <tr>
-            <td width="180" class="required">
-                <?php echo __('Default Role');?>:
-            </td>
-            <td>
-                <select name="role_id">
-                    <option value="0"><?php echo __('Select One'); ?></option>
-                    <?php
-                    foreach ($roles as $id => $role) {
-                        $sel = ($info['role_id'] == $id) ? 'selected="selected"' : '';
-                        echo sprintf('<option value="%d" %s>%s</option>',
-                                $id, $sel, $role);
-                    } ?>
-                </select>
-                &nbsp;<span class="error">*&nbsp;<?php echo $errors['role_id']; ?></span>
-                <i class="help-tip icon-question-sign" href="#role"></i>
-            </td>
-        </tr>
-    </tbody>
-    <tbody>
-        <tr>
-            <th colspan="7">
-                <em><strong><?php echo __('Internal Notes'); ?></strong> </em>
-            </th>
-        </tr>
-        <tr>
-            <td colspan="7"><textarea name="notes" class="richtext no-bar"
-                rows="6" cols="80"><?php
-                echo $info['notes']; ?></textarea>
-            </td>
-        </tr>
-    </tbody>
-    </table>
-</div>
-<div id="departments" class="tab_content" style="display:none">
-   <table class="form_table" width="940" border="0" cellspacing="0" cellpadding="2">
-    <thead>
-        <tr>
-            <th colspan=2>
-                <em><?php echo __('Check departments the group is allowed to access and optionally select an effective role.') ?></em>
-            </th>
-        </tr>
-        <tr>
-            <th width="40%"><?php echo __('Department'); ?></th>
-            <th><?php echo __('Group Role'); ?></th>
-        </tr>
-    </thead>
-    <tbody>
-        <?php
-        foreach (Dept::getDepartments() as $deptId => $name) { ?>
-         <tr>
-            <td>
-             &nbsp;
-             <label>
-              <?php
-              $ck = ($info['depts'] && in_array($deptId, $info['depts'])) ? 'checked="checked"' : '';
-              echo sprintf('%s&nbsp;&nbsp;%s',
-                        sprintf('<input type="checkbox" class="dept-ckb"
-                            name="depts[]" value="%s" %s />',
-                            $deptId, $ck),
-                        Format::htmlchars($name));
-              ?>
-             </label>
-            </td>
-            <td>
-                <?php
-                $DeptAccess = $group ? $group->getDepartmentsAccess() : array();
-                $_name = 'dept'.$deptId.'_role_id';
-                ?>
-                <select name="<?php echo $_name; ?>">
-                    <option value="0">&mdash; <?php
-                    echo __('Group Default'); ?><?php
-                    if (isset($group)) echo ' ('.$group->role->getName().')';
-                    ?> &mdash;</option>
-                    <?php
-                    foreach ($roles as $rid => $role) {
-                        $sel = '';
-                        if (isset($info[$_name]))
-                            $sel = ($info[$_name] == $rid) ? 'selected="selected"' : '';
-                        elseif ($DeptAccess && isset($DeptAccess[$deptId]))
-                            $sel = ($DeptAccess[$deptId] == $rid) ?  'selected="selected"' : '';
-
-                        echo sprintf('<option value="%d" %s>%s</option>',
-                                $rid, $sel, $role);
-                    } ?>
-                </select>
-                <i class="help-tip icon-question-sign" href="#dept-role"></i>
-            </td>
-         </tr>
-         <?php
-        } ?>
-    </tbody>
-    <tfoot>
-     <tr>
-        <td colspan="2">
-            <?php echo __('Select');?>:&nbsp;
-            <a id="selectAll" href="#dept-ckb"><?php echo __('All');?></a>&nbsp;&nbsp;
-            <a id="selectNone" href="#dept-ckb"><?php echo __('None');?></a>&nbsp;&nbsp;
-            <a id="selectToggle" href="#dept-ckb"><?php echo __('Toggle');?></a>&nbsp;&nbsp;
-        </td>
-     </tr>
-    </tfoot>
-   </table>
-</div>
-<p class="centered">
-    <input type="submit" name="submit" value="<?php echo $submit_text; ?>">
-    <input type="reset"  name="reset"  value="<?php echo __('Reset'); ?>">
-    <input type="button" name="cancel" value="<?php echo __('Cancel'); ?>"
-        onclick='window.location.href="?"'>
-</p>
-</form>
diff --git a/include/staff/groups.inc.php b/include/staff/groups.inc.php
deleted file mode 100644
index c1ec406f9510422ae1d5bfac84e9f4c3d55cdeae..0000000000000000000000000000000000000000
--- a/include/staff/groups.inc.php
+++ /dev/null
@@ -1,175 +0,0 @@
-<?php
-if (!defined('OSTADMININC') || !$thisstaff || !$thisstaff->isAdmin())
-    die('Access Denied');
-
-$qs = array();
-$sortOptions = array(
-        'name'   => 'name',
-        'users'  => 'members_count',
-        'depts'  => 'depts_count',
-        'status' => 'isenabled',
-        'created'=> 'created',
-        'updated'=> 'updated');
-
-$orderWays = array('DESC'=>'DESC', 'ASC'=>'ASC');
-$sort = ($_REQUEST['sort'] && $sortOptions[strtolower($_REQUEST['sort'])]) ? strtolower($_REQUEST['sort']) : 'name';
-
-//Sorting options...
-if ($sort && $sortOptions[$sort]) {
-    $order_column = $sortOptions[$sort];
-}
-
-$order_column = $order_column ? $order_column : 'name';
-
-if ($_REQUEST['order'] && isset($orderWays[strtoupper($_REQUEST['order'])])) {
-    $order = $orderWays[strtoupper($_REQUEST['order'])];
-} else {
-    $order = 'ASC';
-}
-
-if ($order_column && strpos($order_column,',')) {
-    $order_column=str_replace(','," $order,",$order_column);
-}
-$x=$sort.'_sort';
-$$x=' class="'.strtolower($order).'" ';
-$page = ($_GET['p'] && is_numeric($_GET['p'])) ? $_GET['p'] : 1;
-$count = Group::objects()->count();
-$pageNav = new Pagenate($count, $page, PAGE_LIMIT);
-$qstr = '&amp;'. Http::build_query($qs);
-$qstr .= '&amp;order='.($order=='DESC' ? 'ASC' : 'DESC');
-$qs += array('sort' => $_REQUEST['sort'], 'order' => $_REQUEST['order']);
-$pageNav->setURL('pages.php', $qs);
-$showing = $pageNav->showing().' '._N('group', 'groups', $count);
-?>
-<div class="pull-left" style="width:700px;padding-top:5px;">
- <h2><?php echo __('Agent Groups');?>
-    <i class="help-tip icon-question-sign" href="#groups"></i>
-    </h2>
- </div>
-<div class="pull-right flush-right" style="padding-top:5px;padding-right:5px;">
-    <b><a href="groups.php?a=add" class="Icon newgroup"><?php echo __('Add New Group');?></a></b></div>
-<div class="clear"></div>
-<form action="groups.php" method="POST" name="groups">
- <?php csrf_token(); ?>
- <input type="hidden" name="do" value="mass_process" >
- <input type="hidden" id="action" name="a" value="" >
- <table class="list" border="0" cellspacing="1" cellpadding="0" width="940">
-    <caption><?php echo $showing; ?></caption>
-    <thead>
-        <tr>
-            <th width="7px">&nbsp;</th>
-            <th width="200"><a <?php echo $name_sort; ?> href="groups.php?<?php echo $qstr; ?>&sort=name"><?php echo __('Group Name');?></a></th>
-            <th width="80"><a  <?php echo $status_sort; ?> href="groups.php?<?php echo $qstr; ?>&sort=status"><?php echo __('Status');?></a></th>
-            <th width="80" style="text-align:center;"><a  <?php echo $users_sort; ?>href="groups.php?<?php echo $qstr; ?>&sort=users"><?php echo __('Members');?></a></th>
-            <th width="80" style="text-align:center;"><a  <?php echo $depts_sort; ?>href="groups.php?<?php echo $qstr; ?>&sort=depts"><?php echo __('Departments');?></a></th>
-            <th width="100"><a  <?php echo $created_sort; ?> href="groups.php?<?php echo $qstr; ?>&sort=created"><?php echo __('Created On');?></a></th>
-            <th width="120"><a  <?php echo $updated_sort; ?> href="groups.php?<?php echo $qstr; ?>&sort=updated"><?php echo __('Last Updated');?></a></th>
-        </tr>
-    </thead>
-    <tbody>
-    <?php
-        $total=0;
-        $ids = ($errors && is_array($_POST['ids'])) ? $_POST['ids'] : null;
-        if ($count) {
-            $groups= Group::objects()
-                ->annotate(array(
-                        'members_count'=>SqlAggregate::COUNT('members__staff_id', true),
-                        'depts_count'=>SqlAggregate::COUNT('depts', true),
-                        'isenabled'=>new SqlExpr(array(
-                                'flags__hasbit' => Group::FLAG_ENABLED))
-                ))
-                ->order_by(sprintf('%s%s',
-                            strcasecmp($order, 'DESC') ? '' : '-',
-                            $order_column))
-                ->limit($pageNav->getLimit())
-                ->offset($pageNav->getStart());
-
-            foreach ($groups as $group) {
-                $sel=false;
-                $id = $group->getId();
-                if($ids && in_array($id, $ids))
-                    $sel=true;
-                ?>
-            <tr id="<?php echo $id; ?>">
-                <td width=7px>
-                  <input type="checkbox" class="ckb" name="ids[]"
-                    value="<?php echo $id; ?>"
-                    <?php echo $sel?'checked="checked"':''; ?>> </td>
-                <td><a href="groups.php?id=<?php echo $id; ?>"><?php echo
-                $group->getName(); ?></a> &nbsp;</td>
-                <td>&nbsp;<?php echo $group->isenabled ? __('Active') : '<b>'.__('Disabled').'</b>'; ?></td>
-                <td style="text-align:right;padding-right:30px">&nbsp;&nbsp;
-                    <?php if ($num=$group->members_count) { ?>
-                        <a href="staff.php?gid=<?php echo $id; ?>"><?php echo $num; ?></a>
-                    <?php } else { ?> 0
-                    <?php } ?>
-                    &nbsp;
-                </td>
-                <td style="text-align:right;padding-right:30px">&nbsp;&nbsp;
-                    <?php echo $group->depts_count; ?>
-                </td>
-                <td><?php echo Format::date($group->getCreateDate()); ?>&nbsp;</td>
-                <td><?php echo Format::datetime($group->getUpdateDate()); ?>&nbsp;</td>
-            </tr>
-            <?php
-            } //end of while.
-        } ?>
-    <tfoot>
-     <tr>
-        <td colspan="7">
-            <?php if ($count) { ?>
-            <?php echo __('Select');?>:&nbsp;
-            <a id="selectAll" href="#ckb"><?php echo __('All');?></a>&nbsp;&nbsp;
-            <a id="selectNone" href="#ckb"><?php echo __('None');?></a>&nbsp;&nbsp;
-            <a id="selectToggle" href="#ckb"><?php echo __('Toggle');?></a>&nbsp;&nbsp;
-            <?php }else{
-                echo __('No groups found!');
-            } ?>
-        </td>
-     </tr>
-    </tfoot>
-</table>
-<?php
-if ($count):
-    echo '<div>&nbsp;'.__('Page').':'.$pageNav->getPageLinks().'&nbsp;</div>';
-?>
-<p class="centered" id="actions">
-    <input class="button" type="submit" name="enable" value="<?php echo __('Enable');?>" >
-    <input class="button" type="submit" name="disable" value="<?php echo __('Disable');?>" >
-    <input class="button" type="submit" name="delete" value="<?php echo __('Delete');?>">
-</p>
-<?php
-endif;
-?>
-</form>
-
-<div style="display:none;" class="dialog" id="confirm-action">
-    <h3><?php echo __('Please Confirm');?></h3>
-    <a class="close" href=""><i class="icon-remove-circle"></i></a>
-    <hr/>
-    <p class="confirm-action" style="display:none;" id="enable-confirm">
-        <?php echo sprintf(__('Are you sure you want to <b>enable</b> %s?'),
-            _N('selected group', 'selected groups', 2));?>
-    </p>
-    <p class="confirm-action" style="display:none;" id="disable-confirm">
-        <?php echo sprintf(__('Are you sure you want to <b>disable</b> %s?'),
-            _N('selected group', 'selected groups', 2));?>
-    </p>
-    <p class="confirm-action" style="display:none;" id="delete-confirm">
-        <font color="red"><strong><?php echo sprintf(__('Are you sure you want to DELETE %s?'),
-            _N('selected group', 'selected groups', 2));?></strong></font>
-        <br><br><?php echo __("Deleted data CANNOT be recovered and might affect agents' access.");?>
-    </p>
-    <div><?php echo __('Please confirm to continue.');?></div>
-    <hr style="margin-top:1em"/>
-    <p class="full-width">
-        <span class="buttons pull-left">
-            <input type="button" value="<?php echo __('No, Cancel');?>" class="close">
-        </span>
-        <span class="buttons pull-right">
-            <input type="button" value="<?php echo __('Yes, Do it!');?>" class="confirm">
-        </span>
-     </p>
-    <div class="clear"></div>
-</div>
-
diff --git a/include/staff/org-view.inc.php b/include/staff/org-view.inc.php
index 94e658d24c39cb02253dfb2d2538be5d6f369421..becfd7adbe80894e81f77e3fae0b7baff9b24680 100644
--- a/include/staff/org-view.inc.php
+++ b/include/staff/org-view.inc.php
@@ -9,20 +9,20 @@ if(!defined('OSTSCPINC') || !$thisstaff || !is_object($org)) die('Invalid path')
              title="Reload"><i class="icon-refresh"></i> <?php echo $org->getName(); ?></a></h2>
         </td>
         <td width="50%" class="right_align has_bottom_border">
-<?php if ($thisstaff->getRole()->hasPerm(Organization::PERM_EDIT)) { ?>
+<?php if ($thisstaff->hasPerm(Organization::PERM_EDIT)) { ?>
             <span class="action-button pull-right" data-dropdown="#action-dropdown-more">
                 <i class="icon-caret-down pull-right"></i>
                 <span ><i class="icon-cog"></i> <?php echo __('More'); ?></span>
             </span>
 <?php } ?>
-<?php if ($thisstaff->getRole()->hasPerm(Organization::PERM_DELETE)) { ?>
+<?php if ($thisstaff->hasPerm(Organization::PERM_DELETE)) { ?>
             <a id="org-delete" class="action-button pull-right org-action"
             href="#orgs/<?php echo $org->getId(); ?>/delete"><i class="icon-trash"></i>
             <?php echo __('Delete Organization'); ?></a>
 <?php } ?>
             <div id="action-dropdown-more" class="action-dropdown anchor-right">
               <ul>
-<?php if ($thisstaff->getRole()->hasPerm(Organization::PERM_EDIT)) { ?>
+<?php if ($thisstaff->hasPerm(Organization::PERM_EDIT)) { ?>
                 <li><a href="#ajax.php/orgs/<?php echo $org->getId();
                     ?>/forms/manage" onclick="javascript:
                     $.dialog($(this).attr('href').substr(1), 201);
@@ -42,13 +42,13 @@ if(!defined('OSTSCPINC') || !$thisstaff || !is_object($org)) die('Invalid path')
                 <tr>
                     <th width="150"><?php echo __('Name'); ?>:</th>
                     <td>
-<?php if ($thisstaff->getRole()->hasPerm(Organization::PERM_EDIT)) { ?>
+<?php if ($thisstaff->hasPerm(Organization::PERM_EDIT)) { ?>
                     <b><a href="#orgs/<?php echo $org->getId();
                     ?>/edit" class="org-action"><i
                         class="icon-edit"></i>
 <?php }
                     echo $org->getName();
-    if ($thisstaff->getRole()->hasPerm(Organization::PERM_EDIT)) { ?>
+    if ($thisstaff->hasPerm(Organization::PERM_EDIT)) { ?>
                     </a></b>
 <?php } ?>
                     </td>
diff --git a/include/staff/orgs.inc.php b/include/staff/orgs.inc.php
index bc41711ff8b59016163c65248071aef835107011..b614eb84c460f61399144151fdc6350ad7ff1b42 100644
--- a/include/staff/orgs.inc.php
+++ b/include/staff/orgs.inc.php
@@ -89,14 +89,14 @@ $_SESSION['orgs_qs_'.$qhash] = $query;
  </div>
 
 <div class="pull-right">
-<?php if ($thisstaff->getRole()->hasPerm(Organization::PERM_CREATE)) { ?>
+<?php if ($thisstaff->hasPerm(Organization::PERM_CREATE)) { ?>
     <a class="action-button add-org"
         href="#">
         <i class="icon-plus-sign"></i>
         <?php echo __('Add Organization'); ?>
     </a>
 <?php }
-if ($thisstaff->getRole()->hasPerm(Organization::PERM_DELETE)) { ?>
+if ($thisstaff->hasPerm(Organization::PERM_DELETE)) { ?>
     <span class="action-button" data-dropdown="#action-dropdown-more"
         style="/*DELME*/ vertical-align:top; margin-bottom:0">
         <i class="icon-caret-down pull-right"></i>
diff --git a/include/staff/role.inc.php b/include/staff/role.inc.php
index 0e982e044bc04cd69ea92d9568f3ef51ef7ba68b..de2eecb2ba4583b396b2c8bd324f424d8baff2f9 100644
--- a/include/staff/role.inc.php
+++ b/include/staff/role.inc.php
@@ -80,13 +80,22 @@ $info = Format::htmlchars(($errors && $_POST) ? array_merge($info, $_POST) : $in
     </thead>
     <tbody>
         <?php
-
         $setting = $role ? $role->getPermissionInfo() : array();
-        foreach (RolePermission::allPermissions() as $g => $perms) { ?>
-         <tr><th><?php
+
+        // Eliminate groups without any department-specific permissions
+        $buckets = array();
+        foreach (RolePermission::allPermissions() as $g => $perms) {
+            foreach ($perms as $k => $v) {
+                if ($v['primary'])
+                    continue;
+                $buckets[$g][$k] = $v;
+            }
+        }
+        foreach ($buckets as $g => $perms) { ?>
+        <tr><th><?php
              echo Format::htmlchars(__($g)); ?></th></tr>
-         <?php
-         foreach($perms as $k => $v)  { ?>
+<?php
+          foreach ($perms as $k => $v) { ?>
           <tr>
             <td>
               <label>
@@ -98,11 +107,6 @@ $info = Format::htmlchars(($errors && $_POST) ? array_merge($info, $_POST) : $in
               &nbsp;&nbsp;
               <?php echo Format::htmlchars(__($v['title'])); ?>
               —
-              <?php
-              if ($v['primary']) { ?>
-              <i class="icon-globe faded" title="<?php echo
-                  __('This permission only applies to the staff primary role'); ?>"></i>
-<?php         } ?>
               <em><?php echo Format::htmlchars(__($v['desc']));
               ?></em>
              </label>
diff --git a/include/staff/staff.inc.php b/include/staff/staff.inc.php
index babf07db01da01030d2625fa3592a2807a9f1b19..659ab6873c2ba23f7d68ac8de189a95143ca58d4 100644
--- a/include/staff/staff.inc.php
+++ b/include/staff/staff.inc.php
@@ -2,360 +2,476 @@
 if(!defined('OSTADMININC') || !$thisstaff || !$thisstaff->isAdmin()) die('Access Denied');
 
 $info = $qs = array();
-if($staff && $_REQUEST['a']!='add'){
+if ($staff && $_REQUEST['a']!='add'){
     //Editing Department.
-    $title=__('Update Agent');
+    $title=__('Manage Agent');
     $action='update';
     $submit_text=__('Save Changes');
-    $passwd_text=__('To reset the password enter a new one below');
-    $info=$staff->getInfo();
-    $info['id']=$staff->getId();
+    $info = $staff->getInfo();
+    $info['id'] = $staff->getId();
     $info['teams'] = $staff->getTeams();
     $info['signature'] = Format::viewableImages($info['signature']);
     $qs += array('id' => $staff->getId());
-}else {
-    $title=__('Add New Agent');
-    $action='create';
-    $submit_text=__('Add Agent');
-    $passwd_text=__('Temporary password required only for "Local" authentication');
-    //Some defaults for new staff.
-    $info['change_passwd']=1;
-    $info['welcome_email']=1;
-    $info['isactive']=1;
-    $info['isvisible']=1;
-    $info['isadmin']=0;
-    $qs += array('a' => 'add');
 }
-$info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
+$info = Format::htmlchars($info);
 ?>
+
 <form action="staff.php?<?php echo Http::build_query($qs); ?>" method="post" id="save" autocomplete="off">
- <?php csrf_token(); ?>
- <input type="hidden" name="do" value="<?php echo $action; ?>">
- <input type="hidden" name="a" value="<?php echo Format::htmlchars($_REQUEST['a']); ?>">
- <input type="hidden" name="id" value="<?php echo $info['id']; ?>">
- <h2><?php echo __('Agent Account');?></h2>
- <table class="form_table" width="940" border="0" cellspacing="0" cellpadding="2">
-    <thead>
-        <tr>
-            <th colspan="2">
-                <h4><?php echo $title; ?></h4>
-                <em><strong><?php echo __('User Information');?></strong></em>
-            </th>
-        </tr>
-    </thead>
-    <tbody>
-        <tr>
-            <td width="180" class="required">
-                <?php echo __('Username');?>:
-            </td>
-            <td>
-                <input type="text" size="30" class="staff-username typeahead"
-                     autofocus name="username" value="<?php echo $info['username']; ?>">
-                &nbsp;<span class="error">*&nbsp;<?php echo $errors['username']; ?></span>&nbsp;<i class="help-tip icon-question-sign" href="#username"></i>
-            </td>
-        </tr>
+  <?php csrf_token(); ?>
+  <input type="hidden" name="do" value="<?php echo $action; ?>">
+  <input type="hidden" name="a" value="<?php echo Format::htmlchars($_REQUEST['a']); ?>">
+  <input type="hidden" name="id" value="<?php echo $info['id']; ?>">
 
+  <h2><?php echo $title; ?>
+    <div>
+      <small><?php echo $info['firstname'].' '.$info['lastname'];?></small>
+    </div>
+  </h2>
+
+  <ul class="clean tabs">
+    <li class="active"><a href="#account"><?php echo __('Account'); ?></a></li>
+    <li><a href="#access"><?php echo __('Access'); ?></a></li>
+    <li><a href="#permissions"><?php echo __('Permisions'); ?></a></li>
+    <li><a href="#teams"><?php echo __('Teams'); ?></a></li>
+  </ul>
+
+  <div class="tab_content" id="account">
+    <table class="table two-column" width="940" border="0" cellspacing="0" cellpadding="2">
+      <tbody>
         <tr>
-            <td width="180" class="required">
-                <?php echo __('First Name');?>:
-            </td>
-            <td>
-                <input type="text" size="30" name="firstname" class="auto first"
-                     value="<?php echo $info['firstname']; ?>">
-                &nbsp;<span class="error">*&nbsp;<?php echo $errors['firstname']; ?></span>&nbsp;
-            </td>
-        </tr>
-        <tr>
-            <td width="180" class="required">
-                <?php echo __('Last Name');?>:
-            </td>
-            <td>
-                <input type="text" size="30" name="lastname" class="auto last"
-                    value="<?php echo $info['lastname']; ?>">
-                &nbsp;<span class="error">*&nbsp;<?php echo $errors['lastname']; ?></span>&nbsp;
-            </td>
+          <td class="required"><?php echo __('Name'); ?>:</td>
+          <td>
+            <input type="text" size="20" maxlength="64" style="width: 145px" name="firstname"
+              autofocus value="<?php echo $info['firstname']; ?>"
+              placeholder="<?php echo __("First Name"); ?>" />
+            <input type="text" size="20" maxlength="64" style="width: 145px" name="lastname"
+              value="<?php echo $info['lastname']; ?>"
+              placeholder="<?php echo __("Last Name"); ?>" />
+            <div class="error"><?php echo $errors['firstname']; ?></div>
+            <div class="error"><?php echo $errors['lastname']; ?></div>
+          </td>
         </tr>
         <tr>
-            <td width="180" class="required">
-                <?php echo __('Email Address');?>:
-            </td>
-            <td>
-                <input type="text" size="30" name="email" class="auto email"
-                    value="<?php echo $info['email']; ?>">
-                &nbsp;<span class="error">*&nbsp;<?php echo $errors['email']; ?></span>&nbsp;<i class="help-tip icon-question-sign" href="#email_address"></i>
-            </td>
+          <td class="required"><?php echo __('Email Address'); ?>:</td>
+          <td>
+            <input type="email" size="40" maxlength="64" style="width: 300px" name="email"
+              value="<?php echo $info['email']; ?>"
+              placeholder="<?php echo __('e.g. me@mycompany.com'); ?>" />
+            <div class="error"><?php echo $errors['email']; ?></div>
+          </td>
         </tr>
         <tr>
-            <td width="180">
-                <?php echo __('Phone Number');?>:
-            </td>
-            <td>
-                <input type="text" size="18" name="phone" class="auto phone"
-                    value="<?php echo $info['phone']; ?>">
-                &nbsp;<span class="error">&nbsp;<?php echo $errors['phone']; ?></span>
-                <?php echo __('Ext');?> <input type="text" size="5" name="phone_ext" value="<?php echo $info['phone_ext']; ?>">
-                &nbsp;<span class="error">&nbsp;<?php echo $errors['phone_ext']; ?></span>
-            </td>
+          <td><?php echo __('Phone Number');?>:</td>
+          <td>
+            <input type="tel" size="18" name="phone" class="auto phone"
+              value="<?php echo $info['phone']; ?>" />
+            <?php echo __('Ext');?>
+            <input type="text" size="5" name="phone_ext"
+              value="<?php echo $info['phone_ext']; ?>">
+            <div class="error"><?php echo $errors['phone']; ?></div>
+            <div class="error"><?php echo $errors['phone_ext']; ?></div>
+          </td>
         </tr>
         <tr>
-            <td width="180">
-                <?php echo __('Mobile Number');?>:
-            </td>
-            <td>
-                <input type="text" size="18" name="mobile" class="auto mobile"
-                    value="<?php echo $info['mobile']; ?>">
-                &nbsp;<span class="error">&nbsp;<?php echo $errors['mobile']; ?></span>
-            </td>
+          <td><?php echo __('Mobile Number');?>:</td>
+          <td>
+            <input type="tel" size="18" name="mobile" class="auto phone"
+              value="<?php echo $info['mobile']; ?>" />
+            <div class="error"><?php echo $errors['mobile']; ?></div>
+          </td>
         </tr>
-<?php if (!$staff) { ?>
-        <tr>
-            <td width="180"><?php echo __('Welcome Email'); ?></td>
-            <td><input type="checkbox" name="welcome_email" id="welcome-email" <?php
-                if ($info['welcome_email']) echo 'checked="checked"';
-                ?> onchange="javascript:
-                var sbk = $('#backend-selection');
-                if ($(this).is(':checked'))
-                    $('#password-fields').hide();
-                else if (sbk.val() == '' || sbk.val() == 'local')
-                    $('#password-fields').show();
-                " />
-                <?php echo __('Send sign in information'); ?>
-                &nbsp;<i class="help-tip icon-question-sign" href="#welcome_email"></i>
-            </td>
+      </tbody>
+      <!-- ================================================ -->
+      <tbody>
+        <tr class="header">
+          <th colspan="2">
+            <?php echo __('Authentication'); ?>
+          </th>
         </tr>
-<?php } ?>
         <tr>
-            <th colspan="2">
-                <em><strong><?php echo __('Authentication'); ?></strong>: <?php echo $passwd_text; ?> &nbsp;<span class="error">&nbsp;<?php echo $errors['temppasswd']; ?></span>&nbsp;<i class="help-tip icon-question-sign" href="#account_password"></i></em>
-            </th>
+          <td class="required"><?php echo __('Username'); ?>:
+            <span class="error">*</span></td>
+          <td>
+            <input type="text" size="40" style="width:300px"
+              class="staff-username typeahead"
+              name="username" value="<?php echo $info['username']; ?>" />
+            <button type="button" class="action-button">
+              <i class="icon-refresh"></i> <?php echo __('Set Password'); ?>
+            </button>
+            <i class="offset help-tip icon-question-sign" href="#username"></i>
+            <div class="error"><?php echo $errors['username']; ?></div>
+          </td>
         </tr>
+<?php
+$bks = array();
+foreach (StaffAuthenticationBackend::allRegistered() as $ab) {
+  if (!$ab->supportsInteractiveAuthentication()) continue;
+  $bks[] = $ab;
+}
+if (count($bks) > 1) {
+?>
         <tr>
-            <td><?php echo __('Authentication Backend'); ?></td>
-            <td>
-            <select name="backend" id="backend-selection" onchange="javascript:
+          <td><?php echo __('Authentication Backend'); ?>:</td>
+          <td>
+            <select name="backend" id="backend-selection"
+              style="width:300px" onchange="javascript:
                 if (this.value != '' && this.value != 'local')
                     $('#password-fields').hide();
                 else if (!$('#welcome-email').is(':checked'))
                     $('#password-fields').show();
                 ">
-                <option value="">&mdash; <?php echo __('Use any available backend'); ?> &mdash;</option>
-            <?php foreach (StaffAuthenticationBackend::allRegistered() as $ab) {
-                if (!$ab->supportsInteractiveAuthentication()) continue; ?>
-                <option value="<?php echo $ab::$id; ?>" <?php
-                    if ($info['backend'] == $ab::$id)
-                        echo 'selected="selected"'; ?>><?php
-                    echo $ab->getName(); ?></option>
-            <?php } ?>
+              <option value="">&mdash; <?php echo __('Use any available backend'); ?> &mdash;</option>
+<?php foreach ($bks as $ab) { ?>
+              <option value="<?php echo $ab::$id; ?>" <?php
+                if ($info['backend'] == $ab::$id)
+                  echo 'selected="selected"'; ?>><?php
+                echo $ab->getName(); ?></option>
+<?php } ?>
             </select>
-            </td>
+          </td>
         </tr>
-    </tbody>
-    <tbody id="password-fields" style="<?php
-        if ($info['welcome_email'] || ($info['backend'] && $info['backend'] != 'local'))
-            echo 'display:none;'; ?>">
-        <tr>
-            <td width="180">
-                <?php echo __('Password');?>:
-            </td>
-            <td>
-                <input type="password" size="18" name="passwd1" value="<?php echo $info['passwd1']; ?>">
-                &nbsp;<span class="error">&nbsp;<?php echo $errors['passwd1']; ?></span>
-            </td>
+<?php
+} ?>
+      </tbody>
+      <!-- ================================================ -->
+      <tbody>
+        <tr class="header">
+          <th colspan="2">
+            <?php echo __('Status and Settings'); ?>
+          </th>
         </tr>
         <tr>
-            <td width="180">
-                <?php echo __('Confirm Password');?>:
-            </td>
-            <td>
-                <input type="password" size="18" name="passwd2" value="<?php echo $info['passwd2']; ?>">
-                &nbsp;<span class="error">&nbsp;<?php echo $errors['passwd2']; ?></span>
-            </td>
+          <td colspan="2">
+            <div class="error"><?php echo $errors['isadmin']; ?></div>
+            <div class="error"><?php echo $errors['isactive']; ?></div>
+            <label>
+            <input type="checkbox" name="islocked" value="1"
+              <?php echo (!$staff->isactive) ? 'checked="checked"' : ''; ?> />
+              <?php echo __('Locked'); ?>
+            </label>
+            <br/>
+            <label>
+            <input type="checkbox" name="isadmin" value="1"
+              <?php echo ($info['isadmin']) ? 'checked="checked"' : ''; ?> />
+              <?php echo __('Administrator'); ?>
+            </label>
+            <br/>
+            <label>
+            <input type="checkbox" name="assigned_only"
+              <?php echo ($info['assigned_only']) ? 'checked="checked"' : ''; ?> />
+              <?php echo __('Limit ticket access to ONLY assigned tickets'); ?>
+            </label>
+            <br/>
+            <label>
+            <input type="checkbox" name="onvacation"
+              <?php echo ($info['onvacation']) ? 'checked="checked"' : ''; ?> />
+              <?php echo __('Vacation Mode'); ?>
+            </label>
+            <br/>
         </tr>
+      </tbody>
+    </table>
 
-        <tr>
-            <td width="180">
-                <?php echo __('Forced Password Change');?>:
-            </td>
-            <td>
-                <input type="checkbox" name="change_passwd" value="0" <?php echo $info['change_passwd']?'checked="checked"':''; ?>>
-                <?php echo __('<strong>Force</strong> password change on next login.');?>
-                &nbsp;<i class="help-tip icon-question-sign" href="#forced_password_change"></i>
-            </td>
-        </tr>
-    </tbody>
-    <tbody>
-        <tr>
-            <th colspan="2">
-                <em><strong><?php echo __("Agent's Signature");?></strong>:
-                <?php echo __('Optional signature used on outgoing emails.');?>
-                &nbsp;<span class="error">&nbsp;<?php echo $errors['signature']; ?></span></em>
-                &nbsp;<i class="help-tip icon-question-sign" href="#agents_signature"></i></em>
-            </th>
-        </tr>
-        <tr>
-            <td colspan=2>
-                <textarea class="richtext no-bar" name="signature" cols="21"
-                    rows="5" style="width: 60%;"><?php echo $info['signature']; ?></textarea>
-                <br><em><?php echo __('Signature is made available as a choice, on ticket reply.');?></em>
-            </td>
-        </tr>
-        <tr>
-            <th colspan="2">
-                <em><strong><?php echo __('Account Status & Settings');?></strong>: <?php echo __('Department and group assigned control access permissions.');?></em>
-            </th>
-        </tr>
-        <tr>
-            <td width="180" class="required">
-                <?php echo __('Account Type');?>:
-            </td>
-            <td>
-                <input type="radio" name="isadmin" value="1" <?php echo $info['isadmin']?'checked="checked"':''; ?>>
-                    <font color="red"><strong><?php echo __('Admin');?></strong></font>
-                <input type="radio" name="isadmin" value="0" <?php echo !$info['isadmin']?'checked="checked"':''; ?>><strong><?php echo __('Agent');?></strong>
-                &nbsp;<span class="error">&nbsp;<?php echo $errors['isadmin']; ?></span>
-            </td>
-        </tr>
-        <tr>
-            <td width="180" class="required">
-                <?php echo __('Account Status');?>:
-            </td>
-            <td>
-                <input type="radio" name="isactive" value="1" <?php echo $info['isactive']?'checked="checked"':''; ?>><strong><?php echo __('Active');?></strong>
-                <input type="radio" name="isactive" value="0" <?php echo !$info['isactive']?'checked="checked"':''; ?>><strong><?php echo __('Locked');?></strong>
-                &nbsp;<span class="error">&nbsp;<?php echo $errors['isactive']; ?></span>&nbsp;<i class="help-tip icon-question-sign" href="#account_status"></i>
-            </td>
-        </tr>
-        <tr>
-            <td width="180" class="required">
-                <?php echo __('Primary Department');?>:
-            </td>
-            <td>
-                <select name="dept_id" id="dept_id" data-quick-add="department">
-                    <option value="0">&mdash; <?php echo __('Select Department');?> &mdash;</option>
-                    <?php
-                    foreach (Dept::getDepartments() as $id=>$name) {
-                        $sel=($info['dept_id']==$id)?'selected="selected"':'';
-                        echo sprintf('<option value="%d" %s>%s</option>',$id,$sel,$name);
-                    }
-                    ?>
-                    <option value="0" data-quick-add>&mdash; <?php echo __('Add New');?> &mdash;</option>
-                </select>
-                &nbsp;<span class="error">*</span>
-                &nbsp;<i class="help-tip icon-question-sign" href="#primary_department"></i>
-                <div class="error"><?php echo $errors['dept_id']; ?></div>
-            </td>
-        </tr>
-        <tr>
-            <td width="180" class="required">
-                <?php echo __('Primary Role');?>:
-            </td>
-            <td>
-                <select name="role_id">
-                    <option value="0">&mdash; <?php echo __('Select Role');?> &mdash;</option>
-                    <?php
-                    foreach (Role::getRoles() as $id=>$name) {
-                        $sel=($info['role_id']==$id)?'selected="selected"':'';
-                        echo sprintf('<option value="%d" %s>%s</option>',$id,$sel,$name);
-                    }
-                    ?>
-                </select>
-                &nbsp;<span class="error">*</span>
-                &nbsp;<i class="help-tip icon-question-sign" href="#primary_role"></i>
-                <div class="error"><?php echo $errors['role_id']; ?></div>
-            </td>
+    <div style="padding:8px 3px; margin-top: 1.6em">
+        <strong class="big"><?php echo __('Internal Notes');?></strong><br/>
+        <?php echo __("be liberal, they're internal.");?>
+    </div>
+
+    <textarea name="notes" class="richtext">
+      <?php echo $info['notes']; ?>
+    </textarea>
+  </div>
+
+  <!-- ============== DEPARTMENT ACCESS =================== -->
+
+  <div class="hidden tab_content" id="access">
+    <table class="table two-column" width="940" border="0" cellspacing="0" cellpadding="2">
+      <tbody>
+        <tr class="header">
+          <th colspan="2">
+            <?php echo __('Primary Department and Role'); ?>
+            <span class="error">*</span>
+            <div><small><?php echo __(
+            "Select the departments the agent is allowed to access and optionally select an effective role."
+          ); ?>
+            </small></div>
+          </th>
         </tr>
         <tr>
-            <td width="180" class="required">
-                <?php echo __('Assigned Group');?>:
-            </td>
-            <td>
-                <select name="group_id" id="group_id">
-                    <option value="0">&mdash; <?php echo __('Select Group');?> &mdash;</option>
-                    <?php
-                    foreach (Group::getGroups() as $id=>$name) {
-                        $sel=($info['group_id']==$id)?'selected="selected"':'';
-                        echo sprintf('<option value="%d" %s>%s</option>',
-                            $id, $sel, $name);
-                    }
-                    ?>
-                </select>
-                &nbsp;<span class="error">*&nbsp;<?php echo $errors['group_id']; ?></span>&nbsp;<i class="help-tip icon-question-sign" href="#assigned_group"></i>
-            </td>
+          <td>
+            <select name="dept_id" id="dept_id" data-quick-add="department">
+              <option value="0">&mdash; <?php echo __('Select Department');?> &mdash;</option>
+              <?php
+              foreach (Dept::getDepartments() as $id=>$name) {
+                $sel=($info['dept_id']==$id)?'selected="selected"':'';
+                echo sprintf('<option value="%d" %s>%s</option>',$id,$sel,$name);
+              }
+              ?>
+              <option value="0" data-quick-add>&mdash; <?php echo __('Add New');?> &mdash;</option>
+            </select>
+            <i class="offset help-tip icon-question-sign" href="#primary_department"></i>
+            <div class="error"><?php echo $errors['dept_id']; ?></div>
+            <div class="error"><?php echo $errors['role_id']; ?></div>
+          </td>
+          <td>
+            <select name="role_id">
+              <option value="0">&mdash; <?php echo __('Select Role');?> &mdash;</option>
+              <?php
+              foreach (Role::getRoles() as $id=>$name) {
+                $sel=($info['role_id']==$id)?'selected="selected"':'';
+                echo sprintf('<option value="%d" %s>%s</option>',$id,$sel,$name);
+              }
+              ?>
+            </select>
+            <i class="offset help-tip icon-question-sign" href="#primary_role"></i>
+          </td>
         </tr>
-        <tr>
-            <td width="180">
-                <?php echo __('Time Zone');?>:
-            </td>
-            <td>
-                <?php
-                $TZ_NAME = 'timezone';
-                $TZ_TIMEZONE = $info['timezone'];
-                include STAFFINC_DIR.'templates/timezone.tmpl.php'; ?>
-                <div class="error"><?php echo $errors['timezone']; ?></div>
-            </td>
+      </tbody>
+      <tbody>
+        <tr id="extended_access_template" class="hidden">
+          <td>
+            <input type="hidden" data-name="dept_access[]" value="" />
+          </td>
+          <td>
+            <select data-name="dept_access_role">
+              <option value="0">&mdash; <?php echo __('Select Role');?> &mdash;</option>
+              <?php
+              foreach (Role::getRoles() as $id=>$name) {
+                echo sprintf('<option value="%d" %s>%s</option>',$id,$sel,$name);
+              }
+              ?>
+            </select>
+            <span style="display:inline-block;width:20px"> </span>
+            <input type="checkbox" data-name="dept_access_alerts" value="1" />
+            <?php echo __('Alerts'); ?>
+            <a href="#" class="pull-right drop-access" title="<?php echo __('Delete');
+              ?>"><i class="icon-trash"></i></a>
+          </td>
         </tr>
-        <tr>
-            <td width="180">
-                <?php echo __('Limited Access');?>:
-            </td>
-            <td>
-                <input type="checkbox" name="assigned_only" value="1" <?php echo $info['assigned_only']?'checked="checked"':''; ?>><?php echo __('Limit ticket access to ONLY assigned tickets.');?>
-                &nbsp;<i class="help-tip icon-question-sign" href="#limited_access"></i>
-            </td>
+      </tbody>
+      <tbody>
+        <tr class="header">
+          <th colspan="2">
+            <?php echo __('Extended Access'); ?>
+          </th>
         </tr>
-        <tr>
-            <td width="180">
-                <?php echo __('Directory Listing');?>:
-            </td>
-            <td>
-                <input type="checkbox" name="isvisible" value="1" <?php echo $info['isvisible']?'checked="checked"':''; ?>>&nbsp;<?php
-                echo __('Make visible in the Agent Directory'); ?>
-                &nbsp;<i class="help-tip icon-question-sign" href="#directory_listing"></i>
-            </td>
+<?php
+$depts = Dept::getDepartments();
+foreach ($staff->dept_access as $dept_access) {
+  unset($depts[$dept_access->dept_id]);
+}
+?>
+        <tr id="add_extended_access">
+          <td colspan="2">
+            <i class="icon-plus-sign"></i>
+            <select id="add_access" data-quick-add="department">
+              <option value="0">&mdash; <?php echo __('Select Department');?> &mdash;</option>
+              <?php
+              foreach ($depts as $id=>$name) {
+                echo sprintf('<option value="%d">%s</option>',$id,$name);
+              }
+              ?>
+              <option value="0" data-quick-add>&mdash; <?php echo __('Add New');?> &mdash;</option>
+            </select>
+            <button type="button" class="action-button">
+              <?php echo __('Add'); ?>
+            </button>
+          </td>
         </tr>
+      </tbody>
+    </table>
+  </div>
+
+  <!-- ================= PERMISSIONS ====================== -->
+
+  <div id="permissions" class="hidden">
+<?php
+    $permissions = array();
+    foreach (RolePermission::allPermissions() as $g => $perms) {
+        foreach ($perms as $k=>$P) {
+            if (!$P['primary'])
+                continue;
+            if (!isset($permissions[$g]))
+                $permissions[$g] = array();
+            $permissions[$g][$k] = $P;
+        }
+    }
+?>
+    <ul class="alt tabs">
+<?php
+    $first = true;
+    foreach ($permissions as $g => $perms) { ?>
+      <li <?php if ($first) { echo 'class="active"'; $first=false; } ?>>
+        <a href="#<?php echo Format::slugify($g); ?>"><?php echo Format::htmlchars(__($g));?></a>
+      </li>
+<?php } ?>
+    </ul>
+<?php
+    $first = true;
+    foreach ($permissions as $g => $perms) { ?>
+    <div class="tab_content <?php if (!$first) { echo 'hidden'; } else { $first = false; }
+      ?>" id="<?php echo Format::slugify($g); ?>">
+      <table class="table">
+<?php foreach ($perms as $k => $v) { ?>
         <tr>
-            <td width="180">
-                <?php echo __('Vacation Mode');?>:
-            </td>
-            <td>
-                <input type="checkbox" name="onvacation" value="1" <?php echo $info['onvacation']?'checked="checked"':''; ?>>
-                    <?php echo __('Change Status to Vacation Mode'); ?>
-                    &nbsp;<i class="help-tip icon-question-sign" href="#vacation_mode"></i>
-            </td>
+          <td>
+            <label>
+            <?php
+            echo sprintf('<input type="checkbox" name="perms[]" value="%s" %s />',
+              $k, ($staff->hasPerm($k)) ? 'checked="checked"' : '');
+            ?>
+            &nbsp;
+            <?php echo Format::htmlchars(__($v['title'])); ?>
+            —
+            <em><?php echo Format::htmlchars(__($v['desc'])); ?></em>
+           </label>
+          </td>
         </tr>
-        <?php
-        // List team assignments.
-        $teams = Team::getTeams();
-        if (count($teams)) { ?>
-        <tr>
-            <th colspan="2">
-                <em><strong><?php echo __('Assigned Teams');?></strong>: <?php echo __("Agent will have access to tickets assigned to a team they belong to regardless of the ticket's department.");?> </em>
-            </th>
+<?php   } ?>
+      </table>
+    </div>
+<?php } ?>
+  </div>
+
+  <!-- ============== TEAM MEMBERSHIP =================== -->
+
+  <div class="hidden tab_content" id="teams">
+    <table class="table two-column" width="100%">
+      <tbody>
+        <tr class="header">
+          <th colspan="2">
+            <?php echo __('Assigned Teams'); ?>
+            <div><small><?php echo __(
+            "Agent will have access to tickets assigned to a team they belong to regardless of the ticket's department. Alerts can be enabled for each associated team."
+            ); ?>
+            </small></div>
+          </th>
         </tr>
-        <?php
-            foreach ($teams as $id=>$name) {
-                $checked=($info['teams'] && in_array($id,$info['teams']))
-                    ? 'checked="checked"' : '';
-                echo sprintf('<tr><td colspan=2><input type="checkbox" name="teams[]" value="%d" %s> %s</td></tr>',
-                     $id,$checked,$name);
-            }
-        } ?>
-        <tr>
-            <th colspan="2">
-                <em><strong><?php echo __('Internal Notes'); ?></strong></em>
-            </th>
+<?php
+$teams = Team::getTeams();
+foreach ($staff->teams as $TM) {
+  unset($teams[$TM->team_id]);
+}
+?>
+        <tr id="join_team">
+          <td colspan="2">
+            <i class="icon-plus-sign"></i>
+            <select id="add_team" data-quick-add="team">
+              <option value="0">&mdash; <?php echo __('Select Team');?> &mdash;</option>
+              <?php
+              foreach ($teams as $id=>$name) {
+                echo sprintf('<option value="%d">%s</option>', $id, $name);
+              }
+              ?>
+              <option value="0" data-quick-add>&mdash; <?php echo __('Add New');?> &mdash;</option>
+            </select>
+            <button type="button" class="action-button">
+              <?php echo __('Add'); ?>
+            </button>
+          </td>
         </tr>
-        <tr>
-            <td colspan=2>
-                <textarea class="richtext no-bar" name="notes" cols="28"
-                    rows="7" style="width: 80%;"><?php echo $info['notes']; ?></textarea>
-            </td>
+      </tbody>
+      <tbody>
+        <tr id="team_member_template" class="hidden">
+          <td>
+            <input type="hidden" data-name="teams[]" value="" />
+          </td>
+          <td>
+            <input type="checkbox" data-name="team_alerts" value="1" />
+            <?php echo __('Alerts'); ?>
+            <a href="#" class="pull-right drop-membership" title="<?php echo __('Delete');
+              ?>"><i class="icon-trash"></i></a>
+          </td>
         </tr>
-    </tbody>
-</table>
-<p style="padding-left:250px;">
-    <input type="submit" name="submit" value="<?php echo $submit_text; ?>">
-    <input type="reset"  name="reset"  value="<?php echo __('Reset');?>">
-    <input type="button" name="cancel" value="<?php echo __('Cancel');?>" onclick='window.location.href="staff.php"'>
-</p>
+      </tbody>
+    </table>
+  </div>
+
+  <p style="text-align:center;">
+      <input type="submit" name="submit" value="<?php echo $submit_text; ?>">
+      <input type="reset"  name="reset"  value="<?php echo __('Reset');?>">
+      <input type="button" name="cancel" value="<?php echo __('Cancel');?>" onclick='window.location.href="helptopics.php"'>
+  </p>
 </form>
+
+<script type="text/javascript">
+var addAccess = function(daid, name, role, alerts, error) {
+  var copy = $('#extended_access_template').clone();
+
+  copy.find('[data-name=dept_access\\[\\]]')
+    .attr('name', 'dept_access[]')
+    .val(daid);
+  copy.find('[data-name^=dept_access_role]')
+    .attr('name', 'dept_access_role['+daid+']')
+    .val(role || 0);
+  copy.find('[data-name^=dept_access_alerts]')
+    .attr('name', 'dept_access_alerts['+daid+']')
+    .prop('checked', alerts);
+  copy.find('td:first').append(document.createTextNode(name));
+  copy.attr('id', '').show().insertBefore($('#add_extended_access'));
+  copy.removeClass('hidden')
+  if (error)
+      $('<div class="error">').text(error).appendTo(copy.find('td:last'));
+};
+
+$('#add_extended_access').find('button').on('click', function() {
+  var selected = $('#add_access').find(':selected');
+  addAccess(selected.val(), selected.text(), 0, true);
+  selected.remove();
+  return false;
+});
+
+$(document).on('click', 'a.drop-access', function() {
+  var tr = $(this).closest('tr');
+  $('#add_access').append(
+    $('<option>')
+    .attr('value', tr.find('input[name^=dept_access][type=hidden]').val())
+    .text(tr.find('td:first').text())
+  );
+  tr.fadeOut(function() { $(this).remove(); });
+  return false;
+});
+
+var joinTeam = function(teamid, name, alerts, error) {
+  var copy = $('#team_member_template').clone();
+
+  copy.find('[data-name=teams\\[\\]]')
+    .attr('name', 'teams[]')
+    .val(teamid);
+  copy.find('[data-name^=team_alerts]')
+    .attr('name', 'team_alerts['+teamid+']')
+    .prop('checked', alerts);
+  copy.find('td:first').append(document.createTextNode(name));
+  copy.attr('id', '').show().insertBefore($('#join_team'));
+  copy.removeClass('hidden');
+  if (error)
+      $('<div class="error">').text(error).appendTo(copy.find('td:last'));
+};
+
+$('#join_team').find('button').on('click', function() {
+  var selected = $('#add_team').find(':selected');
+  joinTeam(selected.val(), selected.text(), true);
+  selected.remove();
+  return false;
+});
+
+<?php
+foreach ($staff->dept_access as $dept_access) {
+  echo sprintf('addAccess(%d, %s, %d, %d, %s);', $dept_access->dept_id,
+    JsonDataEncoder::encode($dept_access->dept->getName()),
+    $dept_access->role_id,
+    $dept_access->isAlertsEnabled(),
+    JsonDataEncoder::encode(@$errors['dept_access'][$dept_access->dept_id])
+  );
+}
+
+foreach ($staff->teams as $member) {
+  echo sprintf('joinTeam(%d, %s, %d, %s);', $member->team_id,
+    JsonDataEncoder::encode($member->team->getName()),
+    $member->isAlertsEnabled(),
+    JsonDataEncoder::encode(@$errors['teams'][$member->team_id])
+  );
+}
+
+?>
+</script>
diff --git a/include/staff/staffmembers.inc.php b/include/staff/staffmembers.inc.php
index d57258241a521e135a479b44fb23119804c92a37..b83c532c06104f4e1560ff0a88c1e32f6b61fd70 100644
--- a/include/staff/staffmembers.inc.php
+++ b/include/staff/staffmembers.inc.php
@@ -8,7 +8,6 @@ $sortOptions = array(
         'name' => array('firstname', 'lastname'),
         'username' => 'username',
         'status' => 'isactive',
-        'group' => 'group__name',
         'dept' => 'dept__name',
         'created' => 'created',
         'login' => 'lastlogin'
@@ -48,11 +47,6 @@ if ($_REQUEST['did'] && is_numeric($_REQUEST['did'])) {
     $qs += array('did' => $_REQUEST['did']);
 }
 
-if ($_REQUEST['gid'] && is_numeric($_REQUEST['gid'])) {
-    $filters += array('group_id' => $_REQUEST['gid']);
-    $qs += array('gid' => $_REQUEST['gid']);
-}
-
 if ($_REQUEST['tid'] && is_numeric($_REQUEST['tid'])) {
     $filters += array('teams__team_id' => $_REQUEST['tid']);
     $qs += array('tid' => $_REQUEST['tid']);
@@ -102,17 +96,6 @@ $agents->limit($pageNav->getLimit())->offset($pageNav->getStart());
              }
              ?>
         </select>
-        <select name="gid" id="gid">
-            <option value="0">&mdash; <?php echo __('All Groups');?> &mdash;</option>
-             <?php
-             if (($groups=Group::getGroups())) {
-                 foreach ($groups as $id => $name) {
-                     $sel=($_REQUEST['gid'] && $_REQUEST['gid']==$id)?'selected="selected"':'';
-                     echo sprintf('<option value="%d" %s>%s</option>',$id,$sel,$name);
-                 }
-             }
-             ?>
-        </select>
         <select name="tid" id="tid">
             <option value="0">&mdash; <?php echo __('All Teams');?> &mdash;</option>
              <?php
@@ -142,7 +125,6 @@ $agents->limit($pageNav->getLimit())->offset($pageNav->getStart());
             <th width="200"><a <?php echo $name_sort; ?> href="staff.php?<?php echo $qstr; ?>&sort=name"><?php echo __('Name');?></a></th>
             <th width="100"><a <?php echo $username_sort; ?> href="staff.php?<?php echo $qstr; ?>&sort=username"><?php echo __('Username');?></a></th>
             <th width="100"><a  <?php echo $status_sort; ?> href="staff.php?<?php echo $qstr; ?>&sort=status"><?php echo __('Status');?></a></th>
-            <th width="120"><a  <?php echo $group_sort; ?>href="staff.php?<?php echo $qstr; ?>&sort=group"><?php echo __('Group');?></a></th>
             <th width="150"><a  <?php echo $dept_sort; ?>href="staff.php?<?php echo $qstr; ?>&sort=dept"><?php echo __('Department');?></a></th>
             <th width="100"><a <?php echo $created_sort; ?> href="staff.php?<?php echo $qstr; ?>&sort=created"><?php echo __('Created');?></a></th>
             <th width="145"><a <?php echo $login_sort; ?> href="staff.php?<?php echo $qstr; ?>&sort=login"><?php echo __('Last Login');?></a></th>
@@ -167,8 +149,7 @@ $agents->limit($pageNav->getLimit())->offset($pageNav->getStart());
                 <td><?php echo $agent->getUserName(); ?></td>
                 <td><?php echo $agent->isActive() ? __('Active') :'<b>'.__('Locked').'</b>'; ?>&nbsp;<?php
                     echo $agent->onvacation ? '<small>(<i>'.__('vacation').'</i>)</small>' : ''; ?></td>
-                <td><a href="groups.php?id=<?php echo $agent->group_id; ?>"><?php
-                    echo Format::htmlchars($agent->group->getName()); ?></a></td>
+
                 <td><a href="departments.php?id=<?php echo
                     $agent->getDeptId(); ?>"><?php
                     echo Format::htmlchars((string) $agent->dept); ?></a></td>
@@ -239,4 +220,3 @@ endif;
      </p>
     <div class="clear"></div>
 </div>
-
diff --git a/include/staff/templates/thread-entry-view.tmpl.php b/include/staff/templates/thread-entry-view.tmpl.php
index 0b5a542bae73f87eadfa31a810487042c05e54f5..06f73f4188f8930845e08af8aaae938508a496c4 100644
--- a/include/staff/templates/thread-entry-view.tmpl.php
+++ b/include/staff/templates/thread-entry-view.tmpl.php
@@ -7,7 +7,7 @@
 <?php
 $E = $entry;
 $i = 0;
-$omniscient = $thisstaff->getRole()->hasPerm(ThreadEntry::PERM_EDIT);
+$omniscient = $thisstaff->hasPerm(ThreadEntry::PERM_EDIT);
 do {
     $i++;
     if (!$omniscient
diff --git a/include/staff/templates/tickets.tmpl.php b/include/staff/templates/tickets.tmpl.php
index 57ced36e88cb1db5f1867a7454f1cf06764c3ce6..d7ee6521b612e7e19a5944fbc2bb7b78c11f165b 100644
--- a/include/staff/templates/tickets.tmpl.php
+++ b/include/staff/templates/tickets.tmpl.php
@@ -9,7 +9,7 @@ elseif ($org) {
     $tickets->filter(array('user__org' => $org));
 }
 
-if (!$thisstaff->getRole()->hasPerm(SearchBackend::PERM_EVERYTHING)) {
+if (!$thisstaff->hasPerm(SearchBackend::PERM_EVERYTHING)) {
     // -- Open and assigned to me
     $visibility = array(
         new Q(array('status__state'=>'open', 'staff_id' => $thisstaff->getId()))
diff --git a/include/staff/templates/user-lookup.tmpl.php b/include/staff/templates/user-lookup.tmpl.php
index 4a8e1c505bf782b04fec58c0b8a606958e10a6d2..3e55a4cb3280c10a606519043d75675365011e21 100644
--- a/include/staff/templates/user-lookup.tmpl.php
+++ b/include/staff/templates/user-lookup.tmpl.php
@@ -5,7 +5,7 @@
 <?php
 if (!isset($info['lookup']) || $info['lookup'] !== false) { ?>
 <div><p id="msg_info"><i class="icon-info-sign"></i>&nbsp; <?php echo
-    $thisstaff->getRole()->hasPerm(User::PERM_CREATE)
+    $thisstaff->hasPerm(User::PERM_CREATE)
     ? __('Search existing users or add a new user.')
     : __('Search existing users.');
 ?></p></div>
@@ -28,7 +28,7 @@ if ($info['error']) {
 <form method="post" class="user" action="<?php echo $info['action'] ?  $info['action'] : '#users/lookup'; ?>">
     <input type="hidden" id="user-id" name="id" value="<?php echo $user ? $user->getId() : 0; ?>"/>
     <i class="icon-user icon-4x pull-left icon-border"></i>
-<?php if ($thisstaff->getRole()->hasPerm(User::PERM_CREATE)) { ?>
+<?php if ($thisstaff->hasPerm(User::PERM_CREATE)) { ?>
     <a class="action-button pull-right" style="overflow:inherit"
         id="unselect-user"  href="#"><i class="icon-remove"></i>
         <?php echo __('Add New User'); ?></a>
@@ -69,7 +69,7 @@ if ($user) { ?>
 </form>
 </div>
 <div id="new-user-form" style="display:<?php echo $user ? 'none' :'block'; ?>;">
-<?php if ($thisstaff->getRole()->hasPerm(User::PERM_CREATE)) { ?>
+<?php if ($thisstaff->hasPerm(User::PERM_CREATE)) { ?>
 <form method="post" class="user" action="<?php echo $info['action'] ?: '#users/lookup/form'; ?>">
     <table width="100%" class="fixed">
     <?php
diff --git a/include/staff/templates/user.tmpl.php b/include/staff/templates/user.tmpl.php
index 5add6b0824d389ff454ded25c57e9b83ce49717c..9400e4f93de8ed5ea0f96d5ffca19ba6a170f0a4 100644
--- a/include/staff/templates/user.tmpl.php
+++ b/include/staff/templates/user.tmpl.php
@@ -50,10 +50,10 @@ if ($info['error']) {
 <div id="user_tabs_container">
 <div class="tab_content" id="info-tab">
 <div class="floating-options">
-<?php if ($thisstaff->getRole()->hasPerm(User::PERM_EDIT)) { ?>
+<?php if ($thisstaff->hasPerm(User::PERM_EDIT)) { ?>
     <a href="<?php echo $info['useredit'] ?: '#'; ?>" id="edituser" class="action" title="<?php echo __('Edit'); ?>"><i class="icon-edit"></i></a>
 <?php }
-      if ($thisstaff->getRole()->hasPerm(User::PERM_DIRECTORY)) { ?>
+      if ($thisstaff->hasPerm(User::PERM_DIRECTORY)) { ?>
     <a href="users.php?id=<?php echo $user->getId(); ?>" title="<?php
         echo __('Manage User'); ?>" class="action"><i class="icon-share"></i></a>
 <?php } ?>
@@ -76,7 +76,7 @@ if ($info['error']) {
 
 <?php if ($org) { ?>
 <div class="hidden tab_content" id="org-tab">
-<?php if ($thisstaff->getRole()->hasPerm(User::PERM_DIRECTORY)) { ?>
+<?php if ($thisstaff->hasPerm(User::PERM_DIRECTORY)) { ?>
 <div class="floating-options">
     <a href="orgs.php?id=<?php echo $org->getId(); ?>" title="<?php
     echo __('Manage Organization'); ?>" class="action"><i class="icon-share"></i></a>
diff --git a/include/staff/templates/users.tmpl.php b/include/staff/templates/users.tmpl.php
index a2111414b06683a14453e8998cd9dc5c0ac9706f..636e3999c49459738d201b1827d853ec4c85e087 100644
--- a/include/staff/templates/users.tmpl.php
+++ b/include/staff/templates/users.tmpl.php
@@ -57,7 +57,7 @@ else
 
 ?>
 <div style="width:700px;" class="pull-left"><b><?php echo $showing; ?></b></div>
-<?php if ($thisstaff->getRole()->hasPerm(User::PERM_EDIT)) { ?>
+<?php if ($thisstaff->hasPerm(User::PERM_EDIT)) { ?>
 <div class="pull-right flush-right" style="padding-right:5px;">
     <b><a href="#orgs/<?php echo $org->getId(); ?>/add-user" class="Icon newstaff add-user"
         ><?php echo __('Add User'); ?></a></b>
diff --git a/include/staff/ticket-view.inc.php b/include/staff/ticket-view.inc.php
index ecad25ee7d8a79e59575551cef4790ecf459da8a..1e0266b105cebdf38e0380135526b66baa37b2c2 100644
--- a/include/staff/ticket-view.inc.php
+++ b/include/staff/ticket-view.inc.php
@@ -231,7 +231,7 @@ if($ticket->isOverdue())
                                                 $user->getId(), sprintf(_N('%d Closed Ticket', '%d Closed Tickets', $closed), $closed));
                                     ?>
                                     <li><a href="tickets.php?a=search&uid=<?php echo $ticket->getOwnerId(); ?>"><i class="icon-double-angle-right icon-fixed-width"></i> <?php echo __('All Tickets'); ?></a></li>
-<?php   if ($thisstaff->getRole()->hasPerm(User::PERM_DIRECTORY)) { ?>
+<?php   if ($thisstaff->hasPerm(User::PERM_DIRECTORY)) { ?>
                                     <li><a href="users.php?id=<?php echo
                                     $user->getId(); ?>"><i class="icon-user
                                     icon-fixed-width"></i> <?php echo __('Manage User'); ?></a></li>
@@ -268,7 +268,7 @@ if($ticket->isOverdue())
                                         'a' => 'search', 'orgid' => $user->getOrgId()
                                     )); ?>"><i class="icon-double-angle-right icon-fixed-width"></i> <?php echo __('All Tickets'); ?></a></li>
 <?php   }
-        if ($thisstaff->getRole()->hasPerm(User::PERM_DIRECTORY)) { ?>
+        if ($thisstaff->hasPerm(User::PERM_DIRECTORY)) { ?>
                                     <li><a href="orgs.php?id=<?php echo $user->getOrgId(); ?>"><i
                                         class="icon-building icon-fixed-width"></i> <?php
                                         echo __('Manage Organization'); ?></a></li>
@@ -770,7 +770,7 @@ $tcount = $ticket->getThreadEntries($types)->count();
                         echo sprintf('<span class="faded">'.__('Ticket is currently in <b>%s</b> department.').'</span>', $ticket->getDeptName());
                     ?>
                     <br>
-                    <select id="deptId" name="deptId">
+                    <select id="deptId" name="deptId" data-quick-add="department">
                         <option value="0" selected="selected">&mdash; <?php echo __('Select Target Department');?> &mdash;</option>
                         <?php
                         if($depts=Dept::getDepartments()) {
@@ -781,6 +781,7 @@ $tcount = $ticket->getThreadEntries($types)->count();
                             }
                         }
                         ?>
+                        <option value="0" data-quick-add>— <?php echo __('Add New'); ?> —</option>
                     </select>&nbsp;<span class='error'>*&nbsp;<?php echo $errors['deptId']; ?></span>
                 </td>
             </tr>
diff --git a/include/staff/tickets.inc.php b/include/staff/tickets.inc.php
index 6c74947473fcdbc92390ac242538a6ff93cf36a0..ce0858e99f06f7834f8c49d1a2de68fd0b9d729b 100644
--- a/include/staff/tickets.inc.php
+++ b/include/staff/tickets.inc.php
@@ -107,7 +107,7 @@ case 'search':
     } elseif (isset($_SESSION['advsearch'])) {
         $form = $search->getFormFromSession('advsearch');
         $tickets = $search->mangleQuerySet($tickets, $form);
-        $view_all_tickets = $thisstaff->getRole()->hasPerm(SearchBackend::PERM_EVERYTHING);
+        $view_all_tickets = $thisstaff->hasPerm(SearchBackend::PERM_EVERYTHING);
         $results_type=__('Advanced Search')
             . '<a class="action-button" href="?clear_filter"><i style="top:0" class="icon-ban-circle"></i> <em>' . __('clear') . '</em></a>';
         $has_relevance = false;
@@ -174,7 +174,7 @@ if (!$view_all_tickets) {
     if ($teams = array_filter($thisstaff->getTeams()))
         $assigned->add(array('team_id__in' => $teams));
 
-    $visibility = Q::any(array('status__state'=>'open', $assigned));
+    $visibility = Q::any(new Q(array('status__state'=>'open', $assigned)));
 
     // -- Routed to a department of mine
     if (!$thisstaff->showAssignedOnly() && ($depts=$thisstaff->getDepts()))
diff --git a/include/staff/user-view.inc.php b/include/staff/user-view.inc.php
index 256654d42816ec1d803c589dce7c00aa1dfde781..7c894ca6dc2fc6ad74657bc3c99f3c5c4cb6ae9b 100644
--- a/include/staff/user-view.inc.php
+++ b/include/staff/user-view.inc.php
@@ -14,18 +14,18 @@ $org = $user->getOrganization();
         </td>
         <td width="50%" class="right_align has_bottom_border">
 <?php if (($account && $account->isConfirmed())
-    || $thisstaff->getRole()->hasPerm(User::PERM_EDIT)) { ?>
+    || $thisstaff->hasPerm(User::PERM_EDIT)) { ?>
             <span class="action-button pull-right" data-dropdown="#action-dropdown-more">
                 <i class="icon-caret-down pull-right"></i>
                 <span><i class="icon-cog"></i> <?php echo __('More'); ?></span>
             </span>
 <?php }
-    if ($thisstaff->getRole()->hasPerm(User::PERM_DELETE)) { ?>
+    if ($thisstaff->hasPerm(User::PERM_DELETE)) { ?>
             <a id="user-delete" class="action-button pull-right user-action"
             href="#users/<?php echo $user->getId(); ?>/delete"><i class="icon-trash"></i>
             <?php echo __('Delete User'); ?></a>
 <?php } ?>
-<?php if ($thisstaff->getRole()->hasPerm(User::PERM_MANAGE)) { ?>
+<?php if ($thisstaff->hasPerm(User::PERM_MANAGE)) { ?>
             <?php
             if ($account) { ?>
             <a id="user-manage" class="action-button pull-right user-action"
@@ -55,7 +55,7 @@ $org = $user->getOrganization();
                         <?php echo __('Send Password Reset Email'); ?></a></li>
                     <?php
                     } ?>
-<?php if ($thisstaff->getRole()->hasPerm(User::PERM_MANAGE)) { ?>
+<?php if ($thisstaff->hasPerm(User::PERM_MANAGE)) { ?>
                     <li><a class="user-action"
                         href="#users/<?php echo $user->getId(); ?>/manage/access"><i
                         class="icon-lock"></i>
@@ -63,7 +63,7 @@ $org = $user->getOrganization();
                 <?php
 }
                 } ?>
-<?php if ($thisstaff->getRole()->hasPerm(User::PERM_EDIT)) { ?>
+<?php if ($thisstaff->hasPerm(User::PERM_EDIT)) { ?>
                 <li><a href="#ajax.php/users/<?php echo $user->getId();
                     ?>/forms/manage" onclick="javascript:
                     $.dialog($(this).attr('href').substr(1), 201);
@@ -85,13 +85,13 @@ $org = $user->getOrganization();
                     <th width="150"><?php echo __('Name'); ?>:</th>
                     <td>
 <?php
-if ($thisstaff->getRole()->hasPerm(User::PERM_EDIT)) { ?>
+if ($thisstaff->hasPerm(User::PERM_EDIT)) { ?>
                     <b><a href="#users/<?php echo $user->getId();
                     ?>/edit" class="user-action"><i
                         class="icon-edit"></i>
 <?php }
                     echo Format::htmlchars($user->getName()->getOriginal());
-if ($thisstaff->getRole()->hasPerm(User::PERM_EDIT)) { ?>
+if ($thisstaff->hasPerm(User::PERM_EDIT)) { ?>
                     </a>
 <?php } ?>
 </td>
@@ -110,7 +110,7 @@ if ($thisstaff->getRole()->hasPerm(User::PERM_EDIT)) { ?>
                             if ($org)
                                 echo sprintf('<a href="#users/%d/org" class="user-action">%s</a>',
                                         $user->getId(), $org->getName());
-                            elseif ($thisstaff->getRole()->hasPerm(User::PERM_EDIT)) {
+                            elseif ($thisstaff->hasPerm(User::PERM_EDIT)) {
                                 echo sprintf(
                                     '<a href="#users/%d/org" class="user-action">%s</a>',
                                     $user->getId(),
diff --git a/include/staff/users.inc.php b/include/staff/users.inc.php
index 5ff926b64d4e00c450f282efebfcca884ae0186a..746ac3683dac1fe0a815d844223389ff3465be92 100644
--- a/include/staff/users.inc.php
+++ b/include/staff/users.inc.php
@@ -74,7 +74,7 @@ $users->order_by($order . $order_column);
  </div>
 
 <div class="pull-right">
-<?php if ($thisstaff->getRole()->hasPerm(User::PERM_CREATE)) { ?>
+<?php if ($thisstaff->hasPerm(User::PERM_CREATE)) { ?>
     <a class="action-button popup-dialog"
         href="#users/add">
         <i class="icon-plus-sign"></i>
@@ -93,12 +93,12 @@ $users->order_by($order . $order_column);
     </span>
     <div id="action-dropdown-more" class="action-dropdown anchor-right">
         <ul>
-<?php if ($thisstaff->getRole()->hasPerm(User::PERM_DELETE)) { ?>
+<?php if ($thisstaff->hasPerm(User::PERM_DELETE)) { ?>
             <li><a class="users-action" href="#delete">
                 <i class="icon-trash icon-fixed-width"></i>
                 <?php echo __('Delete'); ?></a></li>
 <?php }
-if ($thisstaff->getRole()->hasPerm(User::PERM_EDIT)) { ?>
+if ($thisstaff->hasPerm(User::PERM_EDIT)) { ?>
             <li><a href="#orgs/lookup/form" onclick="javascript:
 $.dialog('ajax.php/orgs/lookup/form', 201);
 return false;">
@@ -110,7 +110,7 @@ if ('disabled' != $cfg->getClientRegistrationMode()) { ?>
             <li><a class="users-action" href="#reset">
                 <i class="icon-envelope icon-fixed-width"></i>
                 <?php echo __('Send Password Reset Email'); ?></a></li>
-<?php if ($thisstaff->getRole()->hasPerm(User::PERM_MANAGE)) { ?>
+<?php if ($thisstaff->hasPerm(User::PERM_MANAGE)) { ?>
             <li><a class="users-action" href="#register">
                 <i class="icon-smile icon-fixed-width"></i>
                 <?php echo __('Register'); ?></a></li>
diff --git a/include/upgrader/streams/core/435c62c3-2e7531a2.task.php b/include/upgrader/streams/core/435c62c3-2e7531a2.task.php
index d80bc23a2e616248409c3c3780e3eedfca975799..6a3832f08d960deeb0600ad56e3c62ec4855f52f 100644
--- a/include/upgrader/streams/core/435c62c3-2e7531a2.task.php
+++ b/include/upgrader/streams/core/435c62c3-2e7531a2.task.php
@@ -1,6 +1,9 @@
 <?php
 require_once INCLUDE_DIR.'class.migrater.php';
 
+// Replaced in v1.10 for STAFF_DEPT_TABLE
+define('GROUP_DEPT_TABLE', TABLE_PREFIX.'group_dept_access');
+
 class MigrateGroupDeptAccess extends MigrationTask {
     var $description = "Migrate department access for groups from v1.6";
 
diff --git a/scp/categories.php b/scp/categories.php
index d4e6125b4bca4d3fee6d40544d1ec173c408ff17..3e25c1d672e14f017b26c9da44863cc98f70455b 100644
--- a/scp/categories.php
+++ b/scp/categories.php
@@ -18,7 +18,7 @@ include_once(INCLUDE_DIR.'class.category.php');
 
 /* check permission */
 if(!$thisstaff ||
-        !$thisstaff->getRole()->hasPerm(FAQ::PERM_MANAGE)) {
+        !$thisstaff->hasPerm(FAQ::PERM_MANAGE)) {
     header('Location: kb.php');
     exit;
 }
diff --git a/scp/css/scp.css b/scp/css/scp.css
index af77ddc9812e4638fc59334ea2f0a72639588ec3..42afb11c70fda8aa85eda07d8931f75a0f1955d9 100644
--- a/scp/css/scp.css
+++ b/scp/css/scp.css
@@ -643,18 +643,30 @@ input[type=search] {
 
 .table tr.header td,
 .table th {
-    font-weight: bold;
+    font-weight: normal;
+    font-size: 1.3em;
     text-align: left;
-    height: 24px;
-    background: #f0f0f0;
+    min-height: 24px;
+}
+.table tbody:not(:first-child) th {
+  padding-top: 1.4em;
 }
 
-.table tr {
+.table tr:not(:last-child):not(.header) {
     border-bottom:1px dotted #ddd;
 }
+.table tr.header {
+    border-bottom: 1px dotted #777;
+}
 .table td:not(:empty) {
     height: 24px;
 }
+.table.two-column tbody tr td:first-child {
+    width: 25%;
+}
+.table > tbody > tr.header + tr td {
+  padding-top: 10px;
+}
 
 .form_table {
     margin-top:3px;
@@ -1062,6 +1074,22 @@ ul.tabs li.active {
     bottom: 0;
     box-shadow: 4px -1px 6px -3px rgba(0,0,0,0.2);
 }
+li.error {
+  border-top: 2px solid rgba(255, 0, 0, 0.3) !important;
+}
+li.error.active {
+  border-top-color: rgba(255, 0, 0, 0.7) !important;
+
+}
+li.error a:before {
+  background-color: rgba(255,0,0,0.06);
+  top: 0;
+  left: 0;
+  bottom: 0;
+  right: 0;
+  content: "";
+  position: absolute;
+}
 ul.tabs li:not(.active) {
     box-shadow: inset 0 -5px 10px -9px rgba(0,0,0,0.2);
 }
@@ -1073,6 +1101,7 @@ ul.tabs li a {
     font-weight: normal;
     line-height: 20px;
     color: #444;
+    color: rgba(0,0,0,0.6);
     display: block;
     outline: none;
     padding: 5px 10px;
@@ -1083,6 +1112,8 @@ ul.tabs li a:hover {
 
 ul.tabs li.active a {
     font-weight: bold;
+    color: #222;
+    color: rgba(0,0,0,0.8);
 }
 
 ul.tabs li.empty {
@@ -1141,6 +1172,29 @@ ul.tabs.vertical li a {
     padding: 5px;
 }
 
+ul.tabs.alt {
+  background-color:initial;
+  border-bottom:2px solid #ccc;
+  border-bottom-color: rgba(0,0,0,0.1);
+  box-shadow:none;
+}
+
+ul.tabs.alt li {
+  width:auto;
+  border:none;
+  min-width:0;
+  box-shadow:none;
+  bottom: 1px;
+  height: auto;
+}
+
+ul.tabs.alt li.active {
+  border:none;
+  box-shadow:none;
+  background-color: transparent;
+  border-bottom:2px solid #81a9d7;
+}
+
 #response_options .reply_tab.tell {
     color:#a00 !important;
     background-image:url(../images/reminder.png);
diff --git a/scp/groups.php b/scp/groups.php
deleted file mode 100644
index 494824bb108c2c83b5477ab56ed95d073f772361..0000000000000000000000000000000000000000
--- a/scp/groups.php
+++ /dev/null
@@ -1,142 +0,0 @@
-<?php
-/*********************************************************************
-    groups.php
-
-    User Groups.
-
-    Peter Rotich <peter@osticket.com>
-    Copyright (c)  2006-2013 osTicket
-    http://www.osticket.com
-
-    Released under the GNU General Public License WITHOUT ANY WARRANTY.
-    See LICENSE.TXT for details.
-
-    vim: expandtab sw=4 ts=4 sts=4:
-**********************************************************************/
-require('admin.inc.php');
-
-$group=null;
-if($_REQUEST['id'] && !($group=Group::lookup($_REQUEST['id'])))
-    $errors['err']=sprintf(__('%s: Unknown or invalid ID.'), __('group'));
-
-if($_POST){
-    switch(strtolower($_POST['do'])){
-        case 'update':
-            if (!$group) {
-                $errors['err']=sprintf(__('%s: Unknown or invalid'), __('group'));
-            } elseif (!$_POST['isactive']
-                    && ($thisstaff->getGroupId() == $group->getId())) {
-                $errors['err'] = sprintf(
-                        __("As an admin, you cannot %s a group you belong to - you might lockout all admins!"),
-                        __('disable'));
-            } elseif ($group->update($_POST, $errors)) {
-                $msg=sprintf(__('Successfully updated %s'),
-                    __('this group'));
-            } elseif (!$errors['err']) {
-                $errors['err']=sprintf(__('Unable to update %s. Correct error(s) below and try again!'),
-                    __('this group'));
-            }
-            break;
-        case 'add':
-            $_group = Group::create();
-            if (($_group->update($_POST,$errors))) {
-                $msg=sprintf(__('Successfully added %s'),Format::htmlchars($_POST['name']));
-                $_REQUEST['a']=null;
-            } elseif(!$errors['err']) {
-                $errors['err']=sprintf(__('Unable to add %s. Correct error(s) below and try again.'),
-                    __('this group'));
-            }
-            break;
-        case 'mass_process':
-            $action = strtolower($_POST['a']);
-            if (!$_POST['ids'] || !is_array($_POST['ids']) || !count($_POST['ids'])) {
-                $errors['err'] = sprintf(__('You must select at least %s.'), __('one group'));
-            } elseif(in_array($thisstaff->getGroupId(), $_POST['ids'])
-                    && in_array($action, array('disable', 'delete'))) {
-                $errors['err'] = sprintf(
-                        __("As an admin, you cannot %s a group you belong to - you might lockout all admins!"),
-                        __('disable or delete'));
-            } else {
-                $count = count($_POST['ids']);
-                switch($action) {
-                    case 'enable':
-                        $num = Group::objects()->filter(array(
-                            'id__in' => $_POST['ids']
-                        ))->update(array(
-                            'flags'=> SqlExpression::bitor(
-                                new SqlField('flags'),
-                                Group::FLAG_ENABLED)
-                        ));
-                        if ($num) {
-                            if($num==$count)
-                                $msg = sprintf(__('Successfully activated %s'),
-                                    _N('selected group', 'selected groups', $count));
-                            else
-                                $warn = sprintf(__('%1$d of %2$d %3$s activated'), $num, $count,
-                                    _N('selected group', 'selected groups', $count));
-                        } else {
-                            $errors['err'] = sprintf(__('Unable to activate %s'),
-                                _N('selected group', 'selected groups', $count));
-                        }
-                        break;
-                    case 'disable':
-                        $num = Group::objects()->filter(array(
-                            'id__in' => $_POST['ids']
-                        ))->update(array(
-                            'flags'=> SqlExpression::bitand(
-                                new SqlField('flags'),
-                                (~Group::FLAG_ENABLED))
-                        ));
-
-                        if ($num) {
-                            if($num==$count)
-                                $msg = sprintf(__('Successfully disabled %s'),
-                                    _N('selected group', 'selected groups', $count));
-                            else
-                                $warn = sprintf(__('%1$d of %2$d %3$s disabled'), $num, $count,
-                                    _N('selected group', 'selected groups', $count));
-                        } else {
-                            $errors['err'] = sprintf(__('Unable to disable %s'),
-                                _N('selected group', 'selected groups', $count));
-                        }
-                        break;
-                    case 'delete':
-                        foreach($_POST['ids'] as $k=>$v) {
-                            if(($g=Group::lookup($v)) && $g->delete())
-                                $i++;
-                        }
-
-                        if($i && $i==$count)
-                            $msg = sprintf(__('Successfully deleted %s'),
-                                _N('selected group', 'selected groups', $count));
-                        elseif($i>0)
-                            $warn = sprintf(__('%1$d of %2$d %3$s deleted'), $i, $count,
-                                _N('selected group', 'selected groups', $count));
-                        elseif(!$errors['err'])
-                            $errors['err'] = sprintf(__('Unable to delete %s'),
-                                _N('selected group', 'selected groups', $count));
-                        break;
-                    default:
-                        $errors['err']  = __('Unknown action - get technical help.');
-                }
-            }
-            break;
-        default:
-            $errors['err']=__('Unknown action');
-            break;
-    }
-}
-
-$page='groups.inc.php';
-$tip_namespace = 'staff.groups';
-if($group || ($_REQUEST['a'] && !strcasecmp($_REQUEST['a'],'add'))) {
-    $page='group.inc.php';
-}
-
-$nav->setTabActive('staff');
-$ost->addExtraHeader('<meta name="tip-namespace" content="' . $tip_namespace . '" />',
-    "$('#content').data('tipNamespace', '".$tip_namespace."');");
-require(STAFFINC_DIR.'header.inc.php');
-require(STAFFINC_DIR.$page);
-include(STAFFINC_DIR.'footer.inc.php');
-?>
diff --git a/scp/js/scp.js b/scp/js/scp.js
index 041502735383018b6e848b24a78c181e32ab18a5..cbba7c8e21ff7777f96f5ac5877eb7a4bfce14b1 100644
--- a/scp/js/scp.js
+++ b/scp/js/scp.js
@@ -516,6 +516,11 @@ var scp_prep = function() {
     });
   });
 
+  $('div.tab_content[id] div.error:not(:empty)').each(function() {
+    var div = $(this).closest('.tab_content');
+    $('a[href^=#'+div.attr('id')+']').parent().addClass('error');
+  });
+
   $('[data-toggle="tooltip"]').tooltip()
 };
 
@@ -827,7 +832,9 @@ $(document).on('click.tab', 'ul.tabs li a', function(e) {
     }
     else {
         $tab.addClass('tab_content');
-        $.changeHash($(this).attr('href'), true);
+        // Don't change the URL hash for nested tabs or in dialogs
+        if ($(this).closest('.tab_content, .dialog').length == 0)
+            $.changeHash($(this).attr('href'), true);
     }
 
     if ($tab.length) {
diff --git a/scp/staff.inc.php b/scp/staff.inc.php
index e17314959df935b19883410b89a1a64353e44d65..edbc813006964649e43d1ed6e40cc03282b9fda0 100644
--- a/scp/staff.inc.php
+++ b/scp/staff.inc.php
@@ -35,7 +35,6 @@ define('KB_PREMADE_TABLE',TABLE_PREFIX.'kb_premade');
 /* include what is needed on staff control panel */
 
 require_once(INCLUDE_DIR.'class.staff.php');
-require_once(INCLUDE_DIR.'class.group.php');
 require_once(INCLUDE_DIR.'class.csrf.php');
 
 /* First order of the day is see if the user is logged in and with a valid session.
diff --git a/scp/users.php b/scp/users.php
index 4506941a08a777cbd8e6961045159197e0db99ec..8e88d72a1f00e3debded552b1bb3b98918bffd7b 100644
--- a/scp/users.php
+++ b/scp/users.php
@@ -14,7 +14,7 @@
 **********************************************************************/
 require('staff.inc.php');
 
-if (!$thisstaff->getRole()->hasPerm(User::PERM_DIRECTORY))
+if (!$thisstaff->hasPerm(User::PERM_DIRECTORY))
     Http::redirect('index.php');
 
 require_once INCLUDE_DIR.'class.note.php';
@@ -28,7 +28,7 @@ if ($_POST) {
         case 'update':
             if (!$user) {
                 $errors['err']=sprintf(__('%s: Unknown or invalid'), _N('end user', 'end users', 1));
-            } elseif (!$thisstaff->getRole()->hasPerm(User::PERM_EDIT)) {
+            } elseif (!$thisstaff->hasPerm(User::PERM_EDIT)) {
                 $errors['err'] = __('Action denied. Contact admin for access');
             } elseif(($acct = $user->getAccount())
                     && !$acct->update($_POST, $errors)) {
diff --git a/setup/inc/streams/core/install-mysql.sql b/setup/inc/streams/core/install-mysql.sql
index 368b1bf1cf5af4c948d43ac56e67b497299d9a35..720cb2557760c13c459deda590849b8494ec91c1 100644
--- a/setup/inc/streams/core/install-mysql.sql
+++ b/setup/inc/streams/core/install-mysql.sql
@@ -418,16 +418,6 @@ CREATE TABLE `%TABLE_PREFIX%role` (
   UNIQUE KEY `name` (`name`)
 ) DEFAULT CHARSET=utf8;
 
-DROP TABLE IF EXISTS `%TABLE_PREFIX%group_dept_access`;
-CREATE TABLE `%TABLE_PREFIX%group_dept_access` (
-  `group_id` int(10) unsigned NOT NULL default '0',
-  `dept_id` int(10) unsigned NOT NULL default '0',
-  `role_id` int(10) unsigned NOT NULL default '0',
-  UNIQUE KEY `group_dept` (`group_id`,`dept_id`),
-  KEY `dept_id`  (`dept_id`),
-  KEY `role_id`  (`role_id`)
-) DEFAULT CHARSET=utf8;
-
 DROP TABLE IF EXISTS `%TABLE_PREFIX%help_topic`;
 CREATE TABLE `%TABLE_PREFIX%help_topic` (
   `topic_id` int(11) unsigned NOT NULL auto_increment,
@@ -533,7 +523,6 @@ CREATE TABLE `%TABLE_PREFIX%session` (
 DROP TABLE IF EXISTS `%TABLE_PREFIX%staff`;
 CREATE TABLE `%TABLE_PREFIX%staff` (
   `staff_id` int(11) unsigned NOT NULL auto_increment,
-  `group_id` int(10) unsigned NOT NULL default '0',
   `dept_id` int(10) unsigned NOT NULL default '0',
   `role_id` int(10) unsigned NOT NULL default '0',
   `username` varchar(32) NOT NULL default '',
@@ -562,6 +551,7 @@ CREATE TABLE `%TABLE_PREFIX%staff` (
   `default_signature_type` ENUM( 'none', 'mine', 'dept' ) NOT NULL DEFAULT 'none',
   `default_paper_size` ENUM( 'Letter', 'Legal', 'Ledger', 'A4', 'A3' ) NOT NULL DEFAULT 'Letter',
   `extra` text,
+  `permissions` text,
   `created` datetime NOT NULL,
   `lastlogin` datetime default NULL,
   `passwdreset` datetime default NULL,
@@ -573,6 +563,16 @@ CREATE TABLE `%TABLE_PREFIX%staff` (
   KEY `group_id` (`group_id`,`staff_id`)
 ) DEFAULT CHARSET=utf8;
 
+DROP TABLE IF EXISTS `%TABLE_PREFIX%staff_dept_access`;
+CREATE TABLE `%TABLE_PREFIX%staff_dept_access` (
+  `staff_id` int(10) unsigned NOT NULL DEFAULT '0',
+  `dept_id` int(10) unsigned NOT NULL DEFAULT '0',
+  `role_id` int(10) unsigned NOT NULL DEFAULT '0',
+  `flags` int(10) unsigned NOT NULL DEFAULT '1',
+  PRIMARY KEY `staff_dept` (`staff_id`,`dept_id`),
+  KEY `dept_id` (`dept_id`)
+) DEFAULT CHARSET=utf8;
+
 DROP TABLE IF EXISTS `%TABLE_PREFIX%syslog`;
 CREATE TABLE `%TABLE_PREFIX%syslog` (
   `log_id` int(11) unsigned NOT NULL auto_increment,
@@ -605,7 +605,7 @@ DROP TABLE IF EXISTS `%TABLE_PREFIX%team_member`;
 CREATE TABLE `%TABLE_PREFIX%team_member` (
   `team_id` int(10) unsigned NOT NULL default '0',
   `staff_id` int(10) unsigned NOT NULL,
-  `updated` datetime NOT NULL,
+  `flags` int(10) unsigned NOT NULL DEFAULT '0',
   PRIMARY KEY  (`team_id`,`staff_id`)
 ) DEFAULT CHARSET=utf8;