diff --git a/include/ajax.tickets.php b/include/ajax.tickets.php
index 2d5ed570088cc5c710e15a3b4091d66fddbe55fc..6825556a98ad07028029130e195d7eaa3d7447d5 100644
--- a/include/ajax.tickets.php
+++ b/include/ajax.tickets.php
@@ -942,7 +942,7 @@ function refer($tid, $target=null) {
                 || !$ticket->checkStaffPerm($thisstaff))
             Http::response(404, 'Unknown ticket #');
 
-        $role = $thisstaff->getRole($ticket->getDeptId());
+        $role = $ticket->getRole($thisstaff);
 
         $info = array();
         $state = null;
@@ -995,7 +995,7 @@ function refer($tid, $target=null) {
         elseif ($status->getId() == $ticket->getStatusId())
             $errors['err'] = sprintf(__('Ticket already set to %s status'),
                     __($status->getName()));
-        elseif (($role = $thisstaff->getRole($ticket->getDeptId()))) {
+        elseif (($role = $ticket->getRole($thisstaff))) {
             // Make sure the agent has permission to set the status
             switch(mb_strtolower($status->getState())) {
                 case 'open':
diff --git a/include/class.staff.php b/include/class.staff.php
index 8c943417b3a8cc0a04aaae8a2aba6278061807ad..d3676afb5d71a3783cb87a3332d2a30f28cfdca2 100644
--- a/include/class.staff.php
+++ b/include/class.staff.php
@@ -459,13 +459,21 @@ implements AuthenticatedUser, EmailContact, TemplateVariable, Searchable {
         return $this->_roles;
     }
 
-    function getRole($dept=null) {
-        $deptId = is_object($dept) ? $dept->getId() : $dept;
+    function getRole($dept=null, $assigned=false) {
+
+        if (is_null($dept))
+            return $this->role;
+
+        if ((!$dept instanceof Dept) && !($dept=Dept::lookup($dept)))
+            return null;
+
+        $deptId = $dept->getId();
         $roles = $this->getRoles();
         if (isset($roles[$deptId]))
             return $roles[$deptId];
 
-        if ($this->usePrimaryRoleOnAssignment())
+        // Default to primary role.
+        if ($assigned && $this->usePrimaryRoleOnAssignment())
             return $this->role;
 
         // View only access
@@ -534,8 +542,13 @@ implements AuthenticatedUser, EmailContact, TemplateVariable, Searchable {
         return ($teamId && in_array($teamId, $this->getTeams()));
     }
 
-    function canAccessDept($deptId) {
-        return ($deptId && in_array($deptId, $this->getDepts()) && !$this->isAccessLimited());
+    function canAccessDept($dept) {
+
+        if (!$dept instanceof Dept)
+            return false;
+
+        return (!$this->isAccessLimited()
+                && in_array($dept->getId(), $this->getDepts()));
     }
 
     function getTeams() {
diff --git a/include/class.task.php b/include/class.task.php
index 1a5860b0b87595d6a045058a511f79a78c903ef6..1456272458d49d78463916f6c85f04ff00cc74cd 100644
--- a/include/class.task.php
+++ b/include/class.task.php
@@ -267,7 +267,7 @@ class Task extends TaskModel implements RestrictedAccess, Threadable {
             return false;
 
         // Check access based on department or assignment
-        if (!$staff->canAccessDept($this->getDeptId())
+        if (!$staff->canAccessDept($this->getDept())
                 && $this->isOpen()
                 && $staff->getId() != $this->getStaffId()
                 && !$staff->isTeamMember($this->getTeamId()))
@@ -279,7 +279,7 @@ class Task extends TaskModel implements RestrictedAccess, Threadable {
             return true;
 
         // Permission check requested -- get role.
-        if (!($role=$staff->getRole($this->getDeptId())))
+        if (!($role=$staff->getRole($this->getDept)))
             return false;
 
         // Check permission based on the effective role
@@ -1330,7 +1330,7 @@ class Task extends TaskModel implements RestrictedAccess, Threadable {
         $task->logEvent('created', null, $thisstaff);
 
         // Get role for the dept
-        $role = $thisstaff->getRole($task->dept_id);
+        $role = $thisstaff->getRole($task->getDept());
         // Assignment
         $assignee = $vars['internal_formdata']['assignee'];
         if ($assignee
diff --git a/include/class.thread.php b/include/class.thread.php
index b06bd0432458f9e062c0a7877274da191d0fb26e..dd10d3a0d326235954f7be5066ccdb5ec182e8c3 100644
--- a/include/class.thread.php
+++ b/include/class.thread.php
@@ -284,7 +284,7 @@ implements Searchable {
 
     function isReferred($to=null, $strict=false) {
 
-        if (is_null($to))
+        if (is_null($to) || !$this->referrals)
             return ($this->referrals);
 
         switch (true) {
@@ -299,12 +299,12 @@ implements Searchable {
                 return false;
 
             // Referred to staff's department
-            if ($this->referrals->findFirst(array(
+            if ($to->getDepts() && $this->referrals->findFirst(array(
                             'object_id__in' => $to->getDepts(),
                             'object_type'   => ObjectModel::OBJECT_TYPE_DEPT)))
                 return true;
             // Referred to staff's  team
-            if ($this->referrals->findFirst(array(
+            if ($to->getTeams() && $this->referrals->findFirst(array(
                             'object_id__in' => $to->getTeams(),
                             'object_type'   => ObjectModel::OBJECT_TYPE_TEAM)))
                 return true;
diff --git a/include/class.thread_actions.php b/include/class.thread_actions.php
index 8cc2d785d2f2c29ab1274e4e349a9c3936e40784..ee0c8b16d9b98868144375ad96a78d60c89503f9 100644
--- a/include/class.thread_actions.php
+++ b/include/class.thread_actions.php
@@ -135,7 +135,7 @@ class TEA_EditThreadEntry extends ThreadEntryAction {
                 && $T->getDept()->getManagerId() == $thisstaff->getId()
             )
             || ($T instanceof Ticket
-                && ($role = $thisstaff->getRole($T->getDeptId(), $T->isAssigned($thisstaff)))
+                && ($role = $T->getRole($thisstaff))
                 && $role->hasPerm(ThreadEntry::PERM_EDIT)
             )
         );
diff --git a/include/class.ticket.php b/include/class.ticket.php
index a6c4d78671420a2cab00eeef80622e9f339c761f..cbfa765800763b6e6c6ea8a7c5a0d9633e4dcc0c 100644
--- a/include/class.ticket.php
+++ b/include/class.ticket.php
@@ -248,7 +248,7 @@ implements RestrictedAccess, Threadable, Searchable {
         if (!$this->isOpen())
             return false;
 
-        if (!$to)
+        if (is_null($to))
             return ($this->getStaffId() || $this->getTeamId());
 
         switch (true) {
@@ -276,30 +276,36 @@ implements RestrictedAccess, Threadable, Searchable {
         return null !== $this->getLock();
     }
 
+    function getRole($staff) {
+        if (!$staff instanceof Staff)
+            return null;
+
+        return $staff->getRole($this->getDept(), $this->isAssigned($staff));
+    }
+
     function checkStaffPerm($staff, $perm=null) {
+
         // Must be a valid staff
-        if (!$staff instanceof Staff && !($staff=Staff::lookup($staff)))
+        if ((!$staff instanceof Staff) && !($staff=Staff::lookup($staff)))
             return false;
 
-        // Check access based on department or assignment
-        if (($staff->showAssignedOnly()
-            || !$staff->canAccessDept($this->getDeptId()))
-            // only open tickets can be considered assigned
-            && $this->isOpen()
-            && $staff->getId() != $this->getStaffId()
-            && !$staff->isTeamMember($this->getTeamId())
-            && !$this->thread->isReferred($staff)
-        ) {
+        // check department access first
+        if (!$staff->canAccessDept($this->getDept())
+                // no restrictions
+                && !$staff->isAccessLimited()
+                // check assignment
+                && !$this->isAssigned($staff)
+                // check referral
+                && !$this->thread->isReferred($staff))
             return false;
-        }
 
         // At this point staff has view access unless a specific permission is
         // requested
         if ($perm === null)
             return true;
 
-        // Permission check requested -- get role.
-        if (!($role=$staff->getRole($this->getDeptId())))
+        // Permission check requested -- get role if any
+        if (!($role=$this->getRole($staff)))
             return false;
 
         // Check permission based on the effective role
@@ -1243,7 +1249,7 @@ implements RestrictedAccess, Threadable, Searchable {
     function setStatus($status, $comments='', &$errors=array(), $set_closing_agent=true) {
         global $thisstaff;
 
-        if ($thisstaff && !($role = $thisstaff->getRole($this->getDeptId())))
+        if ($thisstaff && !($role=$this->getRole($thisstaff)))
             return false;
 
         if ($status && is_numeric($status))
@@ -1682,7 +1688,7 @@ implements RestrictedAccess, Threadable, Searchable {
                     // Is agent on vacation ?
                     && $staff->isAvailable()
                     // Does the agent have access to dept?
-                    && $staff->canAccessDept($dept->getId()))
+                    && $staff->canAccessDept($dept))
                 $this->setStaffId($staff->getId());
             else
                 $this->setStaffId(0); // Clear assignment
@@ -3955,7 +3961,8 @@ implements RestrictedAccess, Threadable, Searchable {
             return false;
 
         if ($vars['deptId']
-            && ($role = $thisstaff->getRole($vars['deptId']))
+            && ($dept=Dept::lookup($vars['deptId']))
+            && ($role = $thisstaff->getRole($dept))
             && !$role->hasPerm(Ticket::PERM_CREATE)
         ) {
             $errors['err'] = sprintf(__('You do not have permission to create a ticket in %s'), __('this department'));
@@ -4030,7 +4037,7 @@ implements RestrictedAccess, Threadable, Searchable {
         $vars['msgId']=$ticket->getLastMsgId();
 
         // Effective role for the department
-        $role = $thisstaff->getRole($ticket->getDeptId());
+        $role = $ticket->getRole($thisstaff);
 
         // post response - if any
         $response = null;
diff --git a/include/staff/templates/status-options.tmpl.php b/include/staff/templates/status-options.tmpl.php
index c8cff18a94cc8b06535f0ecd017a2b87df9fd841..16d0f885bf3bb4813865d8ebbdc3171cf5be49b1 100644
--- a/include/staff/templates/status-options.tmpl.php
+++ b/include/staff/templates/status-options.tmpl.php
@@ -1,5 +1,10 @@
 <?php
 global $thisstaff, $ticket;
+
+$role = $ticket ? $ticket->getRole($thisstaff) : $thisstaff->getRole();
+if ($role && !$role->hasPerm(Ticket::PERM_CLOSE))
+    return;
+
 // Map states to actions
 $actions= array(
         'closed' => array(
@@ -14,9 +19,8 @@ $actions= array(
         );
 
 $states = array('open');
-if ($thisstaff->getRole($ticket ? $ticket->getDeptId() : null)->hasPerm(Ticket::PERM_CLOSE)
-        && (!$ticket || !Ticket::getMissingRequiredFields($ticket)))
-    $states = array_merge($states, array('closed'));
+if (!$ticket || $ticket->isCloseable())
+    $states[] = 'closed';
 
 $statusId = $ticket ? $ticket->getStatusId() : 0;
 $nextStatuses = array();
diff --git a/include/staff/templates/task-view.tmpl.php b/include/staff/templates/task-view.tmpl.php
index 0f6d44adf483efa54507354218d700bdb1c9b1eb..adb250728e1ae3bd19c355e78974120bc39237a5 100644
--- a/include/staff/templates/task-view.tmpl.php
+++ b/include/staff/templates/task-view.tmpl.php
@@ -1,7 +1,7 @@
 <?php
 if (!defined('OSTSCPINC')
     || !$thisstaff || !$task
-    || !($role = $thisstaff->getRole($task->getDeptId())))
+    || !($role = $thisstaff->getRole($task->getDept())))
     die('Invalid path');
 
 global $cfg;
diff --git a/include/staff/templates/ticket-preview.tmpl.php b/include/staff/templates/ticket-preview.tmpl.php
index b615e37971f7fe9b7f4be23bc547192f752f2893..2431b038471221c6d121f74fc44917dadf034c75 100644
--- a/include/staff/templates/ticket-preview.tmpl.php
+++ b/include/staff/templates/ticket-preview.tmpl.php
@@ -6,7 +6,7 @@
 
 $staff=$ticket->getStaff();
 $lock=$ticket->getLock();
-$role=$thisstaff->getRole($ticket->getDeptId());
+$role=$ticket->getRole($thistaff);
 $error=$msg=$warn=null;
 $thread = $ticket->getThread();
 
diff --git a/include/staff/ticket-tasks.inc.php b/include/staff/ticket-tasks.inc.php
index a260f0f05b07e2427b973ccfd68f44ea9ce0e30c..aa765aef5cd39eebf3f68f53f4d02227f5ca4641 100644
--- a/include/staff/ticket-tasks.inc.php
+++ b/include/staff/ticket-tasks.inc.php
@@ -1,7 +1,7 @@
 <?php
 global $thisstaff;
 
-$role = $thisstaff->getRole($ticket->getDeptId());
+$role = $ticket->getRole($thisstaff);
 
 $tasks = Task::objects()
     ->select_related('dept', 'staff', 'team')
diff --git a/include/staff/ticket-view.inc.php b/include/staff/ticket-view.inc.php
index c31d946607c5e6040722de4dad3c3882aebf4524..0767f5ebcd6f5624742965fd34babb0ac1603966 100644
--- a/include/staff/ticket-view.inc.php
+++ b/include/staff/ticket-view.inc.php
@@ -10,7 +10,7 @@ $info=($_POST && $errors)?Format::input($_POST):array();
 
 //Get the goodies.
 $dept  = $ticket->getDept();  //Dept
-$role  = $thisstaff->getRole($dept, $ticket->isAssigned($thisstaff));
+$role  = $ticket->getRole($thisstaff);
 $staff = $ticket->getStaff(); //Assigned or closed by..
 $user  = $ticket->getOwner(); //Ticket User (EndUser)
 $team  = $ticket->getTeam();  //Assigned team.
@@ -186,8 +186,10 @@ if($ticket->isOverdue())
                     return false"
                     ><i class="icon-paste"></i> <?php echo __('Manage Forms'); ?></a></li>
                 <?php
-                } ?>
+                }
 
+                if ($role->hasPerm(Ticket::PERM_REPLY)) {
+                    ?>
                 <li>
 
                     <?php
@@ -199,9 +201,12 @@ if($ticket->isOverdue())
                             $recipients);
                    ?>
                 </li>
+                <?php
+                } ?>
 
 
-<?php           if ($thisstaff->hasPerm(Email::PERM_BANLIST)) {
+<?php           if ($thisstaff->hasPerm(Email::PERM_BANLIST)
+                    && $role->hasPerm(Ticket::PERM_REPLY)) {
                      if(!$emailBanned) {?>
                         <li><a class="confirm-action" id="ticket-banemail"
                             href="#banemail"><i class="icon-ban-circle"></i> <?php echo sprintf(
diff --git a/scp/tasks.php b/scp/tasks.php
index 5cd77777f3714b6ff592878ab25ca6ee35a212a1..63e4b87ca1b35831e54f6bfcde6ce0efe07620db 100644
--- a/scp/tasks.php
+++ b/scp/tasks.php
@@ -44,7 +44,7 @@ if($_POST && !$errors):
     if ($task) {
         //More coffee please.
         $errors=array();
-        $role = $thisstaff->getRole($task->getDeptId());
+        $role = $thisstaff->getRole($task->getDept());
         switch(strtolower($_POST['a'])):
         case 'postnote': /* Post Internal Note */
             $vars = $_POST;
diff --git a/scp/tickets.php b/scp/tickets.php
index 112a7f7d581212a1c16ccaab830468af560e6164..773744a69f47513a5febf1451c4820ce9f3826dd 100644
--- a/scp/tickets.php
+++ b/scp/tickets.php
@@ -139,7 +139,7 @@ if($_POST && !$errors):
         //More coffee please.
         $errors=array();
         $lock = $ticket->getLock(); //Ticket lock if any
-        $role = $thisstaff->getRole($ticket->getDeptId());
+        $role = $ticket->getRole($thisstaff);
         switch(strtolower($_POST['a'])):
         case 'reply':
             if (!$role || !$role->hasPerm(Ticket::PERM_REPLY)) {