diff --git a/include/ajax.staff.php b/include/ajax.staff.php
index 871fe9db7fd6846fbdc3c0f5748930adda85ae40..37c71462caa02ec72b20528ec8b71df8f8c0056e 100644
--- a/include/ajax.staff.php
+++ b/include/ajax.staff.php
@@ -58,4 +58,90 @@ class StaffAjaxAPI extends AjaxController {
 
       include STAFFINC_DIR . 'templates/set-password.tmpl.php';
   }
+
+    function getAgentPerms($id) {
+        global $thisstaff;
+
+        if (!$thisstaff)
+            Http::response(403, 'Agent login required');
+        if (!$thisstaff->isAdmin())
+            Http::response(403, 'Access denied');
+        if (!($staff = Staff::lookup($id)))
+            Http::response(404, 'No such agent');
+
+        return $this->encode($staff->getPermissionInfo());
+    }
+
+    function resetPermissions() {
+        global $ost, $thisstaff;
+
+        if (!$thisstaff)
+            Http::response(403, 'Agent login required');
+        if (!$thisstaff->isAdmin())
+            Http::response(403, 'Access denied');
+
+        $form = new ResetAgentPermissionsForm($_POST);
+
+        if (@is_array($_GET['ids'])) {
+            $perms = new RolePermission();
+            $selected = Staff::objects()->filter(array('staff_id__in' => $_GET['ids']));
+            foreach ($selected as $staff)
+                // XXX: This maybe should be intersection rather than union
+                $perms->merge($staff->getPermission());
+            $form->getField('perms')->setValue($perms->getInfo());
+        }
+
+        if ($_POST && $form->isValid()) {
+            $clean = $form->getClean();
+            Http::response(201, $this->encode(array('perms' => $clean['perms'])));
+        }
+
+        $title = __("Reset Agent Permissions");
+        $verb = __("Continue");
+        $path = ltrim($ost->get_path_info(), '/');
+
+        include STAFFINC_DIR . 'templates/reset-agent-permissions.tmpl.php';
+    }
+
+    function changeDepartment() {
+        global $ost, $thisstaff;
+
+        if (!$thisstaff)
+            Http::response(403, 'Agent login required');
+        if (!$thisstaff->isAdmin())
+            Http::response(403, 'Access denied');
+
+        $form = new ChangeDepartmentForm($_POST);
+
+        // Preselect reasonable dept and role based on the current  settings
+        // of the received staff ids
+        if (@is_array($_GET['ids'])) {
+            $dept_id = null;
+            $role_id = null;
+            $selected = Staff::objects()->filter(array('staff_id__in' => $_GET['ids']));
+            foreach ($selected as $staff) {
+                if (!isset($dept_id)) {
+                    $dept_id = $staff->dept_id;
+                    $role_id = $staff->role_id;
+                }
+                elseif ($dept_id != $staff->dept_id)
+                    $dept_id = 0;
+                elseif ($role_id != $staff->role_id)
+                    $role_id = 0;
+            }
+            $form->getField('dept_id')->setValue($dept_id);
+            $form->getField('role_id')->setValue($role_id);
+        }
+
+        if ($_POST && $form->isValid()) {
+            $clean = $form->getClean();
+            Http::response(201, $this->encode($clean));
+        }
+
+        $title = __("Change Primary Department");
+        $verb = __("Continue");
+        $path = ltrim($ost->get_path_info(), '/');
+
+        include STAFFINC_DIR . 'templates/quick-add.tmpl.php';
+    }
 }
diff --git a/include/class.dept.php b/include/class.dept.php
index 6bb8625a27ff7dd2fd7ef8f8ae3611f480b23fd9..d27e5923c62914880e794168e348d31aac38cafc 100644
--- a/include/class.dept.php
+++ b/include/class.dept.php
@@ -632,10 +632,9 @@ extends Form {
             'pid' => new ChoiceField(array(
                 'label' => '',
                 'default' => 0,
-                'choices' => array_merge(
-                    array(0 => __('Top-Level Department')),
-                    Dept::getDepartments()
-                )
+                'choices' =>
+                    array(0 => '— '.__('Top-Level Department').' —')
+                    + Dept::getDepartments()
             )),
             'name' => new TextboxField(array(
                 'required' => true,
@@ -649,10 +648,9 @@ extends Form {
             'email_id' => new ChoiceField(array(
                 'label' => __('Email Mailbox'),
                 'default' => 0,
-                'choices' => array_merge(
-                    array(0 => '— '.__('System Default').' —'),
-                    Email::getAddresses()
-                ),
+                'choices' =>
+                    array(0 => '— '.__('System Default').' —')
+                    + Email::getAddresses(),
                 'configuration' => array(
                     'classes' => 'span12',
                 ),
diff --git a/include/class.format.php b/include/class.format.php
index 85b1fd9aeefccd11387d4d93f3c26999de6b20ba..54b9ae8f55d259331d04ff3935ff2a5de441fc41 100644
--- a/include/class.format.php
+++ b/include/class.format.php
@@ -777,7 +777,9 @@ class Format {
     }
 
     function relativeTime($to, $from=false, $granularity=1) {
-        $timestamp = $to ?: Misc::gmtime();
+        if (!$to)
+            return false;
+        $timestamp = $to;
         if (gettype($timestamp) === 'string')
             $timestamp = strtotime($timestamp);
         $from = $from ?: Misc::gmtime();
diff --git a/include/class.forms.php b/include/class.forms.php
index 05df629165b05f9576e32b235b428b7d0ec9300b..5cbdcae5b7de73f80ecf70f6253bf4feb8ff0bdb 100644
--- a/include/class.forms.php
+++ b/include/class.forms.php
@@ -828,6 +828,11 @@ class FormField {
     function getAnswer() { return $this->answer; }
     function setAnswer($ans) { $this->answer = $ans; }
 
+    function setValue($value) {
+        $this->reset();
+        $this->getWidget()->value = $value;
+    }
+
     function getFormName() {
         if (is_numeric($this->get('id')))
             return substr(md5(
@@ -3038,6 +3043,8 @@ class BoxChoicesWidget extends Widget {
     }
 
     function emitChoices($choices) {
+      static $uid = 1;
+
       if (!isset($this->value))
           $this->value = $this->field->get('default');
       $config = $this->field->getConfiguration();
@@ -3045,14 +3052,13 @@ class BoxChoicesWidget extends Widget {
       if (isset($config['classes']))
           $classes = 'class="'.$config['classes'].'"';
 
-      $i=0;
       foreach ($choices as $k => $v) {
           if (is_array($v)) {
               $this->renderSectionBreak($k);
               $this->emitChoices($v);
               continue;
           }
-          $id = sprintf("%s-%s", $this->id, $i++);
+          $id = sprintf("%s-%s", $this->id, $uid++);
 ?>
         <label <?php echo $classes; ?>
           for="<?php echo $id; ?>" style="display:block;">
@@ -3487,7 +3493,8 @@ class FreeTextField extends FormField {
 class FreeTextWidget extends Widget {
     function render($options=array()) {
         $config = $this->field->getConfiguration();
-        ?><div class="thread-body" style="padding:0"><?php
+        $class = $config['classes'] ?: 'thread-body'
+        ?><div class="<?php echo $class; ?>" style="padding:0"><?php
         if ($label = $this->field->getLocal('label')) { ?>
             <h3><?php
             echo Format::htmlchars($label);
diff --git a/include/class.nav.php b/include/class.nav.php
index 94f7a7ca44dad7fdec96723d9ee6b29ee9751545..8acff9052bc7d9da1436c2c6e067f50ed802d812 100644
--- a/include/class.nav.php
+++ b/include/class.nav.php
@@ -154,7 +154,7 @@ class StaffNav {
                                             'iconclass'=>'assignedTickets',
                                             'droponly'=>true);
 
-                        if ($staff->hasPerm(TicketModel::PERM_CREATE))
+                        if ($staff->hasPerm(TicketModel::PERM_CREATE, false))
                             $subnav[]=array('desc'=>__('New Ticket'),
                                             'title' => __('Open a New Ticket'),
                                             'href'=>'tickets.php?a=open',
diff --git a/include/class.role.php b/include/class.role.php
index 6e9c42a01870f832f4390c9b67b0dbd5e036a3fd..64fdbe07d2c74aebb608e4fdb30e8bcaaeaf9d74 100644
--- a/include/class.role.php
+++ b/include/class.role.php
@@ -306,6 +306,19 @@ class RolePermission {
         return $this->perms;
     }
 
+    function merge($perms) {
+        if ($perms instanceof self)
+            $perms = $perms->getInfo();
+        foreach ($perms as $perm=>$value) {
+            if (is_numeric($perm)) {
+                // Array of perm names
+                $perm = $value;
+                $value = true;
+            }
+            $this->set($perm, $value);
+        }
+    }
+
     static function allPermissions() {
         return static::$_permissions;
     }
@@ -345,10 +358,9 @@ extends AbstractForm {
             )),
             'clone' => new ChoiceField(array(
                 'default' => 0,
-                'choices' => array_merge(
-                    array(0 => '— '.__('Clone an existing role').' —'),
-                    Role::getRoles()
-                ),
+                'choices' =>
+                    array(0 => '— '.__('Clone an existing role').' —')
+                    + Role::getRoles(),
                 'configuration' => array(
                     'classes' => 'span12',
                 ),
diff --git a/include/class.staff.php b/include/class.staff.php
index bf3e59a3f711ec743d197ce489f52bea0e8d18f7..4b194946f1ff0d8b2aba8888cdd822bc4ec32e1e 100644
--- a/include/class.staff.php
+++ b/include/class.staff.php
@@ -367,6 +367,27 @@ implements AuthenticatedUser, EmailContact, TemplateVariable {
         return $this->dept;
     }
 
+    function setDepartmentId($dept_id, $eavesdrop=false) {
+        // Grant access to the current department
+        $old = $this->dept_id;
+        if ($eavesdrop) {
+            $da = StaffDeptAccess::create(array(
+                'dept_id' => $old,
+                'role_id' => $this->role_id,
+            ));
+            $da->setAlerts(true);
+            $this->dept_access->add($da);
+        }
+
+        // Drop extended access to new department
+        $this->dept_id = $dept_id;
+        if ($da = $this->dept_access->findFirst(array(
+            'dept_id' => $dept_id))
+        ) {
+            $this->dept_access->remove($da);
+        }
+    }
+
     function getLanguage() {
         return (isset($this->lang)) ? $this->lang : false;
     }
@@ -395,8 +416,15 @@ implements AuthenticatedUser, EmailContact, TemplateVariable {
         return $this->role;
     }
 
-    function hasPerm($perm) {
-        return $this->getPermission()->has($perm);
+    function hasPerm($perm, $global=true) {
+        if ($global)
+            return $this->getPermission()->has($perm);
+        if ($this->getRole()->hasPerm($perm))
+            return true;
+        foreach ($this->dept_access as $da)
+            if ($da->role->hasPerm($perm))
+                return true;
+        return false;
     }
 
     function canManageTickets() {
@@ -915,7 +943,7 @@ implements AuthenticatedUser, EmailContact, TemplateVariable {
         }
 
         // Update some things for ::updateAccess to inspect
-        $this->dept_id = $vars['dept_id'];
+        $this->setDepartmentId($vars['dept_id']);
 
         // Format access update as [array(dept_id, role_id, alerts?)]
         $access = array();
@@ -1020,17 +1048,19 @@ implements AuthenticatedUser, EmailContact, TemplateVariable {
         return !$errors;
     }
 
-    private function updatePerms($vars, &$errors) {
+    function updatePerms($vars, &$errors) {
         if (!$vars) {
             $this->permissions = '';
             return;
         }
+        $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();
+        return true;
     }
 
 }
@@ -1133,3 +1163,100 @@ extends AbstractForm {
         return parent::render($staff, false, array('template' => 'dynamic-form-simple.tmpl.php'));
     }
 }
+
+class ResetAgentPermissionsForm
+extends AbstractForm {
+    function buildFields() {
+        $permissions = array();
+        foreach (RolePermission::allPermissions() as $g => $perms) {
+            foreach ($perms as $k => $v) {
+                if (!$v['primary'])
+                    continue;
+                $permissions[$g][$k] = "{$v['title']} — {$v['desc']}";
+            }
+        }
+        return array(
+            'clone' => new ChoiceField(array(
+                'default' => 0,
+                'choices' =>
+                    array(0 => '— '.__('Clone an existing agent').' —'),
+                    + Staff::getStaffMembers(),
+                'configuration' => array(
+                    'classes' => 'span12',
+                ),
+            )),
+            'perms' => new ChoiceField(array(
+                'choices' => $permissions,
+                'widget' => 'TabbedBoxChoicesWidget',
+                'configuration' => array(
+                    'multiple' => true,
+                    'classes' => 'vertical-pad',
+                ),
+            )),
+        );
+    }
+
+    function getClean() {
+        $clean = parent::getClean();
+        // Index permissions as ['ticket.edit' => 1]
+        $clean['perms'] = array_keys($clean['perms']);
+        return $clean;
+    }
+
+    function render($staff=true) {
+        return parent::render($staff, false, array('template' => 'dynamic-form-simple.tmpl.php'));
+    }
+}
+
+class ChangeDepartmentForm
+extends AbstractForm {
+    function buildFields() {
+        return array(
+            'header' => new FreeTextField(array(
+                'configuration' => array(
+                    'content' => __('Change the primary department and primary role of the selected agents'),
+                    'classes' => ' ',
+                )
+            )),
+            'dept_id' => new ChoiceField(array(
+                'default' => 0,
+                'required' => true,
+                'label' => __('Primary Department'),
+                'choices' =>
+                    array(0 => '— '.__('Primary Department').' —')
+                    + Dept::getDepartments(),
+                'configuration' => array(
+                    'classes' => 'span12',
+                ),
+            )),
+            'role_id' => new ChoiceField(array(
+                'default' => 0,
+                'required' => true,
+                'label' => __('Primary Role'),
+                'choices' =>
+                    array(0 => '— '.__('Corresponding Role').' —')
+                    + Role::getRoles(),
+                'configuration' => array(
+                    'classes' => 'span12',
+                ),
+            )),
+            'eavesdrop' => new BooleanField(array(
+                'configuration' => array(
+                    'desc' => __('Maintain access to current primary department'),
+                    'classes' => 'form footer',
+                ),
+            )),
+            // alerts?
+        );
+    }
+
+    function getClean() {
+        $clean = parent::getClean();
+        $clean['eavesdrop'] = $clean['eavesdrop'] ? 1 : 0;
+        return $clean;
+    }
+
+    function render($staff=true) {
+        return parent::render($staff, false, array('template' => 'dynamic-form-simple.tmpl.php'));
+    }
+}
diff --git a/include/class.team.php b/include/class.team.php
index fe80bd4f997f12222b1b76b81b717186f9b0e54a..c7a66058dd0d5b5ce31196fd09e7b49a025ea4bd 100644
--- a/include/class.team.php
+++ b/include/class.team.php
@@ -365,10 +365,9 @@ extends AbstractForm {
             'lead_id' => new ChoiceField(array(
                 'label' => __('Optionally select a leader for the team'),
                 'default' => 0,
-                'choices' => array_merge(
+                'choices' =>
                     array(0 => '— '.__('None').' —'),
-                    Staff::getStaffMembers()
-                ),
+                    + Staff::getStaffMembers(),
                 'configuration' => array(
                     'classes' => 'span12',
                 ),
diff --git a/include/i18n/en_US/role.yaml b/include/i18n/en_US/role.yaml
index 3be09a6392a5c3f8e92d880c18048812b8c77a0f..ca76b2650513d4ffcf0d5bb48fa57cb07b203757 100644
--- a/include/i18n/en_US/role.yaml
+++ b/include/i18n/en_US/role.yaml
@@ -32,18 +32,6 @@
     task.close,
     task.delete,
     canned.manage,
-    faq.manage,
-    stats.agents,
-    emails.banlist,
-    user.create,
-    user.edit,
-    user.delete,
-    user.manage,
-    user.dir,
-    org.create,
-    org.edit,
-    org.delete,
-    search.all,
     thread.edit]
 
 - id: 2
@@ -65,18 +53,7 @@
     task.transfer,
     task.reply,
     task.close,
-    canned.manage,
-    faq.manage,
-    stats.agents,
-    emails.banlist,
-    user.create,
-    user.edit,
-    user.delete,
-    user.manage,
-    user.dir,
-    org.create,
-    org.edit,
-    org.delete]
+    canned.manage]
 
 - id: 3
   flags: 1
@@ -92,9 +69,10 @@
     task.create,
     task.assign,
     task.transfer,
-    task.reply,
-    user.edit,
-    user.manage,
-    user.dir,
-    org.create,
-    org.edit]
+    task.reply]
+
+- id: 4
+  flags: 1
+  name: View only
+  notes: Simple role with no permissions
+  permissions: []
diff --git a/include/staff/staffmembers.inc.php b/include/staff/staffmembers.inc.php
index b83c532c06104f4e1560ff0a88c1e32f6b61fd70..c1f12f9ce409a5560dd29c96803eaafad09b4241 100644
--- a/include/staff/staffmembers.inc.php
+++ b/include/staff/staffmembers.inc.php
@@ -107,13 +107,47 @@ $agents->limit($pageNav->getLimit())->offset($pageNav->getStart());
              }
              ?>
         </select>
-        &nbsp;&nbsp;
-        <input type="submit" name="submit" value="<?php echo __('Apply');?>"/>
+        <input type="submit" name="submit" class="small" value="<?php echo __('Apply');?>"/>
     </form>
  </div>
-<div class="pull-right flush-right" style="padding-right:5px;"><b><a href="staff.php?a=add" class="Icon newstaff"><?php echo __('Add New Agent');?></a></b></div>
-<div class="clear"></div>
-<form action="staff.php" method="POST" name="staff" >
+
+ <form id="mass-actions" action="staff.php" method="POST" name="staff" >
+
+ <div class="pull-right">
+     <a class="action-button" href="staff.php?a=add">
+       <i class="icon-plus-sign"></i>
+        <?php echo __('Add New Agent'); ?>
+     </a>
+     <span class="action-button" data-dropdown="#action-dropdown-more">
+       <i class="icon-caret-down pull-right"></i>
+        <span ><i class="icon-cog"></i> <?php echo __('More');?></span>
+     </span>
+     <div id="action-dropdown-more" class="action-dropdown anchor-right">
+        <ul id="actions">
+          <li><a class="confirm" data-name="enable" href="staff.php?a=enable">
+            <i class="icon-trash icon-fixed-width"></i>
+            <?php echo __('Enable'); ?></a></li>
+          <li><a class="confirm" data-name="disable" href="staff.php?a=disable">
+            <i class="icon-trash icon-fixed-width"></i>
+            <?php echo __('Disable'); ?></a></li>
+          <li><a class="confirm" data-name="delete" href="staff.php?a=delete">
+            <i class="icon-trash icon-fixed-width"></i>
+            <?php echo __('Delete'); ?></a></li>
+          <li><a class="dialog-first" data-action="permissions" href="#staff/reset-permissions">
+            <i class="icon-trash icon-fixed-width"></i>
+            <?php echo __('Reset Permissions'); ?></a></li>
+          <li><a class="dialog-first" data-action="department" href="#staff/change-department">
+            <i class="icon-trash icon-fixed-width"></i>
+            <?php echo __('Change Department'); ?></a></li>
+          <li><a class="dialog-first" href="#staff/reset-access">
+            <i class="icon-trash icon-fixed-width"></i>
+            <?php echo __('Reset Access'); ?></a></li>
+        </ul>
+    </div>
+</div>
+
+<div class="clear" style="padding: 3px 0"></div>
+
  <?php csrf_token(); ?>
  <input type="hidden" name="do" value="mass_process" >
  <input type="hidden" id="action" name="a" value="" >
@@ -145,16 +179,16 @@ $agents->limit($pageNav->getLimit())->offset($pageNav->getStart());
                   <input type="checkbox" class="ckb" name="ids[]"
                   value="<?php echo $id; ?>" <?php echo $sel ? 'checked="checked"' : ''; ?> >
                 <td><a href="staff.php?id=<?php echo $id; ?>"><?php echo
-                Format::htmlchars((string) $agent->getName()); ?></a>&nbsp;</td>
+                Format::htmlchars((string) $agent->getName()); ?></a></td>
                 <td><?php echo $agent->getUserName(); ?></td>
-                <td><?php echo $agent->isActive() ? __('Active') :'<b>'.__('Locked').'</b>'; ?>&nbsp;<?php
+                <td><?php echo $agent->isActive() ? __('Active') :'<b>'.__('Locked').'</b>'; ?><?php
                     echo $agent->onvacation ? '<small>(<i>'.__('vacation').'</i>)</small>' : ''; ?></td>
 
                 <td><a href="departments.php?id=<?php echo
                     $agent->getDeptId(); ?>"><?php
                     echo Format::htmlchars((string) $agent->dept); ?></a></td>
                 <td><?php echo Format::date($agent->created); ?></td>
-                <td><?php echo Format::datetime($agent->lastlogin); ?>&nbsp;</td>
+                <td><?php echo Format::relativeTime(Misc::db2gmtime($agent->lastlogin)) ?: '<em class="faded">'.__('never').'</em>'; ?></td>
                </tr>
             <?php
             } //end of foreach
@@ -175,18 +209,9 @@ $agents->limit($pageNav->getLimit())->offset($pageNav->getStart());
     </tfoot>
 </table>
 <?php
-if ($count): //Show options..
+if ($count) { //Show options..
     echo '<div>&nbsp;'.__('Page').':'.$pageNav->getPageLinks().'&nbsp;</div>';
-?>
-<p class="centered" id="actions">
-    <input class="button" type="submit" name="enable" value="<?php echo __('Enable');?>" >
-    &nbsp;&nbsp;
-    <input class="button" type="submit" name="disable" value="<?php echo __('Lock');?>" >
-    &nbsp;&nbsp;
-    <input class="button" type="submit" name="delete" value="<?php echo __('Delete');?>">
-</p>
-<?php
-endif;
+}
 ?>
 </form>
 
@@ -220,3 +245,34 @@ endif;
      </p>
     <div class="clear"></div>
 </div>
+
+<script type="text/javascript">
+$(document).on('click', 'a.dialog-first', function(e) {
+    e.preventDefault();
+    var action = $(this).data('action'),
+        $form = $('form#mass-actions');
+    if ($(':checkbox.ckb:checked', $form).length == 0) {
+        $.sysAlert(__('Oops'),
+            __('You need to select at least one item'));
+        return false;
+    }
+    ids = $form.find('.ckb');
+    $.dialog('ajax.php/' + $(this).attr('href').substr(1), 201, function (xhr, data) {
+        $form.find('#action').val(action);
+        data = JSON.parse(data);
+        if (data)
+            $.each(data, function(k, v) {
+              if (v.length) {
+                  $.each(v, function() {
+                      $form.append($('<input type="hidden">').attr('name', k+'[]').val(this));
+                  })
+              }
+              else {
+                  $form.append($('<input type="hidden">').attr('name', k).val(v));
+              }
+          });
+          $form.submit();
+    }, { data: ids.serialize()});
+    return false;
+});
+</script>
diff --git a/include/staff/templates/reset-agent-permissions.tmpl.php b/include/staff/templates/reset-agent-permissions.tmpl.php
new file mode 100644
index 0000000000000000000000000000000000000000..b69dbd72309ab78def3c70f1c7a308874c02d4a4
--- /dev/null
+++ b/include/staff/templates/reset-agent-permissions.tmpl.php
@@ -0,0 +1,22 @@
+<?php
+include 'quick-add.tmpl.php';
+$clone = $form->getField('clone')->getWidget()->name;
+$permissions = $form->getField('perms')->getWidget()->name;
+?>
+<script type="text/javascript">
+  $('#_<?php echo $clone; ?>').change(function() {
+    var $this = $(this),
+        id = $this.val(),
+        form = $this.closest('form');
+    $.ajax({
+      url: 'ajax.php/staff/'+id+'/perms',
+      dataType: 'json',
+      success: function(json) {
+        $('[name="<?php echo $permissions; ?>[]"]', form).prop('checked', false);
+        $.each(json, function(k, v) {
+          form.find('[value="'+k+'"]', form).prop('checked', !!v);
+        });
+      }
+    });
+  });
+</script>
diff --git a/include/staff/ticket-open.inc.php b/include/staff/ticket-open.inc.php
index 65d4e37a089a479817311d01380e34a495678a64..6e2f9bae14106e8077ff70b189ea9f3bacdb7472 100644
--- a/include/staff/ticket-open.inc.php
+++ b/include/staff/ticket-open.inc.php
@@ -1,6 +1,6 @@
 <?php
 if (!defined('OSTSCPINC') || !$thisstaff
-        || !$thisstaff->hasPerm(TicketModel::PERM_CREATE))
+        || !$thisstaff->hasPerm(TicketModel::PERM_CREATE, false))
         die('Access Denied');
 
 $info=array();
diff --git a/scp/ajax.php b/scp/ajax.php
index 72e68d429ae8cd633761be9e484fd4f1f84fc9e0..bc5207be392f96fbb062204e98c52b8bdf40639d 100644
--- a/scp/ajax.php
+++ b/scp/ajax.php
@@ -233,8 +233,11 @@ $dispatcher = patterns('',
         )),
         url_get('^/role/(?P<id>\d+)/perms', 'getRolePerms')
     )),
-    url('^/staff/(?P<id>\d+)', patterns('ajax.staff.php:StaffAjaxAPI',
-        url('^/set-password$', 'setPassword')
+    url('^/staff', patterns('ajax.staff.php:StaffAjaxAPI',
+        url('^/(?P<id>\d+)/set-password$', 'setPassword'),
+        url_get('^/(?P<id>\d+)/perms', 'getAgentPerms'),
+        url('^/reset-permissions', 'resetPermissions'),
+        url('^/change-department', 'changeDepartment')
     ))
 );
 
diff --git a/scp/faq.php b/scp/faq.php
index bac34c7ceefe4b8cd1c30bd396c93cab2745bc02..7881dbc18edb42484653846f04a18ad9df66ea60 100644
--- a/scp/faq.php
+++ b/scp/faq.php
@@ -140,17 +140,16 @@ else {
     }
 }
 
-$role = $thisstaff->getRole();
 $inc='faq-categories.inc.php'; //FAQs landing page.
 if($faq) {
     $inc='faq-view.inc.php';
     if ($_REQUEST['a']=='edit'
-            && $role->hasPerm(FAQ::PERM_MANAGE))
+            && $thisstaff->hasPerm(FAQ::PERM_MANAGE))
         $inc='faq.inc.php';
     elseif ($_REQUEST['a'] == 'print')
         return $faq->printPdf();
 }elseif($_REQUEST['a']=='add'
-        && $role->hasPerm(FAQ::PERM_MANAGE)) {
+        && $thisstaff->hasPerm(FAQ::PERM_MANAGE)) {
     $inc='faq.inc.php';
 } elseif($category && $_REQUEST['a']!='search') {
     $inc='faq-category.inc.php';
diff --git a/scp/js/scp.js b/scp/js/scp.js
index 7cf9c4254fb11d4ad5081e378792b241ceddd978..608555ef86998caf916d3d1c5282247cb8207921 100644
--- a/scp/js/scp.js
+++ b/scp/js/scp.js
@@ -103,11 +103,11 @@ var scp_prep = function() {
              });
             $.toggleOverlay(true);
             $('.dialog#confirm-action .confirm-action').hide();
-            $('.dialog#confirm-action p#'+this.name+'-confirm')
+            $('.dialog#confirm-action p#'+name+'-confirm')
             .show()
             .parent('div').show().trigger('click');
         }
-
+        e.preventDefault();
         return false;
      });
 
@@ -596,7 +596,7 @@ $.dialog = function (url, codes, cb, options) {
     $('div#popup-loading', $popup).show()
         .find('h1').css({'margin-top':function() { return $popup.height()/3-$(this).height()/3}});
     $popup.resize().show();
-    $('div.body', $popup).load(url, function () {
+    $('div.body', $popup).load(url, options.data, function () {
         $('div#popup-loading', $popup).hide();
         $('div.body', $popup).slideDown({
             duration: 300,
diff --git a/scp/staff.php b/scp/staff.php
index 17a106e93c41cbe16da59755ffb0b7fdefd9563b..a0426ea28ca43188ed3612126de46f85eb4b1769 100644
--- a/scp/staff.php
+++ b/scp/staff.php
@@ -46,18 +46,18 @@ if($_POST){
             if(!$_POST['ids'] || !is_array($_POST['ids']) || !count($_POST['ids'])) {
                 $errors['err'] = sprintf(__('You must select at least %s.'),
                     __('one agent'));
-            } elseif(in_array($thisstaff->getId(),$_POST['ids'])) {
+            } elseif(in_array($_POST['a'], array('disable', 'delete'))
+                && in_array($thisstaff->getId(),$_POST['ids'])
+            ) {
                 $errors['err'] = __('You can not disable/delete yourself - you could be the only admin!');
             } else {
-                $count=count($_POST['ids']);
+                $count = count($_POST['ids']);
+                $members = Staff::objects()->filter(array(
+                    'staff_id__in' => $_POST['ids']
+                ));
                 switch(strtolower($_POST['a'])) {
                     case 'enable':
-                        $num = Staff::objects()->filter(array(
-                            'staff_id__in' => $_POST['ids']
-                        ))->update(array(
-                            'isactive' => 1
-                        ));
-
+                        $num = $members->update(array('isactive' => 1));
                         if ($num) {
                             if($num==$count)
                                 $msg = sprintf('Successfully activated %s',
@@ -70,13 +70,9 @@ if($_POST){
                                 _N('selected agent', 'selected agents', $count));
                         }
                         break;
-                    case 'disable':
-                        $num = Staff::objects()->filter(array(
-                            'staff_id__in' => $_POST['ids']
-                        ))->update(array(
-                            'isactive' => 0
-                        ));
 
+                    case 'disable':
+                        $num = $members->update(array('isactive' => 0));
                         if ($num) {
                             if($num==$count)
                                 $msg = sprintf('Successfully disabled %s',
@@ -89,10 +85,11 @@ if($_POST){
                                 _N('selected agent', 'selected agents', $count));
                         }
                         break;
+
                     case 'delete':
                         $i = 0;
-                        foreach($_POST['ids'] as $k=>$v) {
-                            if($v!=$thisstaff->getId() && ($s=Staff::lookup($v)) && $s->delete())
+                        foreach($members as $s) {
+                            if ($s->staff_id != $thisstaff->getId() && $s->delete())
                                 $i++;
                         }
 
@@ -106,6 +103,48 @@ if($_POST){
                             $errors['err'] = sprintf(__('Unable to delete %s'),
                                 _N('selected agent', 'selected agents', $count));
                         break;
+
+                    case 'permissions':
+                        foreach ($members as $s)
+                            if ($s->updatePerms($_POST['perms'], $errors) && $s->save())
+                                $i++;
+
+                        if($i && $i==$count)
+                            $msg = sprintf(__('Successfully updated %s'),
+                                _N('selected agent', 'selected agents', $count));
+                        elseif($i>0)
+                            $warn = sprintf(__('%1$d of %2$d %3$s updated'), $i, $count,
+                                _N('selected agent', 'selected agents', $count));
+                        elseif(!$errors['err'])
+                            $errors['err'] = sprintf(__('Unable to update %s'),
+                                _N('selected agent', 'selected agents', $count));
+                        break;
+
+                    case 'department':
+                        if (!$_POST['dept_id'] || !$_POST['role_id']
+                            || !Dept::lookup($_POST['dept_id'])
+                            || !Role::lookup($_POST['role_id'])
+                        ) {
+                            $errors['err'] = 'Internal error.';
+                            break;
+                        }
+                        foreach ($members as $s) {
+                            $s->setDepartmentId((int) $_POST['dept_id'], $_POST['eavesdrop']);
+                            $s->role_id = (int) $_POST['role_id'];
+                            if ($s->save() && $s->dept_access->saveAll())
+                                $i++;
+                        }
+                        if($i && $i==$count)
+                            $msg = sprintf(__('Successfully updated %s'),
+                                _N('selected agent', 'selected agents', $count));
+                        elseif($i>0)
+                            $warn = sprintf(__('%1$d of %2$d %3$s updated'), $i, $count,
+                                _N('selected agent', 'selected agents', $count));
+                        elseif(!$errors['err'])
+                            $errors['err'] = sprintf(__('Unable to update %s'),
+                                _N('selected agent', 'selected agents', $count));
+                        break;
+
                     default:
                         $errors['err'] = __('Unknown action - get technical help.');
                 }
diff --git a/scp/tickets.php b/scp/tickets.php
index 8e1d45185b6eace1655b12fe900be0a825cd95ad..7027667a12d6204df6d6ba69f33fb75e2805351d 100644
--- a/scp/tickets.php
+++ b/scp/tickets.php
@@ -364,7 +364,7 @@ if($_POST && !$errors):
             case 'open':
                 $ticket=null;
                 if (!$thisstaff ||
-                        !$thisstaff->hasPerm(TicketModel::PERM_CREATE)) {
+                        !$thisstaff->hasPerm(TicketModel::PERM_CREATE, false)) {
                      $errors['err'] = sprintf('%s %s',
                              sprintf(__('You do not have permission %s.'),
                                  __('to create tickets')),
@@ -483,7 +483,7 @@ if (isset($_SESSION['advsearch'])) {
                         (!$_REQUEST['status'] || $_REQUEST['status']=='search'));
 }
 
-if ($thisstaff->hasPerm(TicketModel::PERM_CREATE)) {
+if ($thisstaff->hasPerm(TicketModel::PERM_CREATE, false)) {
     $nav->addSubMenu(array('desc'=>__('New Ticket'),
                            'title'=> __('Open a New Ticket'),
                            'href'=>'tickets.php?a=open',
@@ -513,7 +513,7 @@ if($ticket) {
 } else {
 	$inc = 'tickets.inc.php';
     if ($_REQUEST['a']=='open' &&
-            $thisstaff->hasPerm(TicketModel::PERM_CREATE))
+            $thisstaff->hasPerm(TicketModel::PERM_CREATE, false))
         $inc = 'ticket-open.inc.php';
     elseif($_REQUEST['a'] == 'export') {
         $ts = strftime('%Y%m%d');