diff --git a/include/ajax.kbase.php b/include/ajax.kbase.php
index 3554c85bbddc6531f97055b16255d7dabd0bfb19..3909fd831598a3a23c3a4182cf5a3fb9f469a478 100644
--- a/include/ajax.kbase.php
+++ b/include/ajax.kbase.php
@@ -54,7 +54,8 @@ class KbaseAjaxAPI extends AjaxController {
                 $faq->getId(),
                 $faq->getId(),
                 $faq->getNumAttachments());
-        if($thisstaff && $thisstaff->canManageFAQ()) {
+        if($thisstaff
+                && $thisstaff->getRole()->hasPerm(FAQ::PERM_MANAGE)) {
             $resp.=sprintf(' | <a href="faq.php?id=%d&a=edit">'.__('Edit').'</a>',$faq->getId());
 
         }
diff --git a/include/ajax.reports.php b/include/ajax.reports.php
index d432fee88ba7166aff44dba6e0385d89adfedb51..6dbf60df52322a91d8b513fb530e2246eb9c0ede 100644
--- a/include/ajax.reports.php
+++ b/include/ajax.reports.php
@@ -70,7 +70,7 @@ class OverviewReportAjaxAPI extends AjaxController {
                       (T1.staff_id='.db_input($thisstaff->getId())
                         .(($depts=$thisstaff->getManagedDepartments())?
                             (' OR T1.dept_id IN('.implode(',', db_input($depts)).')'):'')
-                        .(($thisstaff->getRole()->canViewStaffStats())?
+                        .($thisstaff->hasPerm(ReportModel::PERM_AGENTS)?
                             (' OR T1.dept_id IN('.implode(',', db_input($thisstaff->getDepts())).')'):'')
                      .')'
                      )
diff --git a/include/ajax.tasks.php b/include/ajax.tasks.php
index ab8bce914e6b87629fc43d12ff14deaaaf137cc5..cb6728a2dbd0eee8245c2050184e4cb1c795cffe 100644
--- a/include/ajax.tasks.php
+++ b/include/ajax.tasks.php
@@ -25,7 +25,8 @@ class TasksAjaxAPI extends AjaxController {
     function preview($tid) {
         global $thisstaff;
 
-        // TODO: check staff's access.
+        // No perm. check -- preview allowed for staff
+        // XXX: perhaps force preview via parent object?
         if(!$thisstaff || !($task=Task::lookup($tid)))
             Http::response(404, __('No such task'));
 
@@ -35,10 +36,12 @@ class TasksAjaxAPI extends AjaxController {
     function edit($tid) {
         global $thisstaff;
 
-        // TODO: check staff's access.
-        if(!$thisstaff || !($task=Task::lookup($tid)))
+        if(!($task=Task::lookup($tid)))
             Http::response(404, __('No such task'));
 
+        if (!$task->checkStaffPerm($thisstaff, Task::PERM_EDIT))
+            Http::response(403, __('Permission Denied'));
+
         $info = $errors = array();
         $forms = DynamicFormEntry::forObject($task->getId(),
                 ObjectModel::OBJECT_TYPE_TASK);
@@ -54,10 +57,12 @@ class TasksAjaxAPI extends AjaxController {
     function transfer($tid) {
         global $thisstaff;
 
-        // TODO: check staff's access.
-        if(!$thisstaff || !($task=Task::lookup($tid)))
+        if(!($task=Task::lookup($tid)))
             Http::response(404, __('No such task'));
 
+        if (!$task->checkStaffPerm($thisstaff, Task::PERM_TRANSFER))
+            Http::response(403, __('Permission Denied'));
+
         $info = $errors = array();
         if ($_POST) {
             if ($task->transfer($_POST,  $errors)) {
@@ -75,10 +80,12 @@ class TasksAjaxAPI extends AjaxController {
     function assign($tid) {
         global $thisstaff;
 
-        // TODO: check staff's access.
-        if(!$thisstaff || !($task=Task::lookup($tid)))
+        if(!($task=Task::lookup($tid)))
             Http::response(404, __('No such task'));
 
+        if (!$task->checkStaffPerm($thisstaff, Task::PERM_ASSIGN))
+            Http::response(403, __('Permission Denied'));
+
         $info = $errors = array();
         if ($_POST) {
             if ($task->assign($_POST,  $errors)) {
@@ -96,10 +103,12 @@ class TasksAjaxAPI extends AjaxController {
    function delete($tid) {
         global $thisstaff;
 
-        // TODO: check staff's access.
-        if(!$thisstaff || !($task=Task::lookup($tid)))
+        if(!($task=Task::lookup($tid)))
             Http::response(404, __('No such task'));
 
+        if (!$task->checkStaffPerm($thisstaff, Task::PERM_DELETE))
+            Http::response(403, __('Permission Denied'));
+
         $info = $errors = array();
         if ($_POST) {
             if ($task->delete($_POST,  $errors)) {
@@ -127,8 +136,8 @@ class TasksAjaxAPI extends AjaxController {
     function task($tid) {
         global $thisstaff;
 
-        // TODO: check staff's access.
-        if (!$thisstaff || !($task=Task::lookup($tid)))
+        if (!($task=Task::lookup($tid))
+                || !$task->checkStaffPerm($thisstaff))
             Http::response(404, __('No such task'));
 
         $info=$errors=array();
diff --git a/include/ajax.tickets.php b/include/ajax.tickets.php
index 6cd63e98ad71c3603754b88e25cc78ee61d1bf4b..96518633461427d866094de07ea98e176368ea45 100644
--- a/include/ajax.tickets.php
+++ b/include/ajax.tickets.php
@@ -103,7 +103,7 @@ class TicketsAjaxAPI extends AjaxController {
         if(!$tid || !is_numeric($tid) || !$thisstaff || !$cfg || !$cfg->getLockTime())
             return 0;
 
-        if(!($ticket = Ticket::lookup($tid)) || !$ticket->checkStaffAccess($thisstaff))
+        if(!($ticket = Ticket::lookup($tid)) || !$ticket->checkStaffPerm($thisstaff))
             return $this->json_encode(array('id'=>0, 'retry'=>false, 'msg'=>__('Lock denied!')));
 
         //is the ticket already locked?
@@ -167,7 +167,8 @@ class TicketsAjaxAPI extends AjaxController {
 
         global $thisstaff;
 
-        if(!$thisstaff || !($ticket=Ticket::lookup($tid)) || !$ticket->checkStaffAccess($thisstaff))
+        if(!$thisstaff || !($ticket=Ticket::lookup($tid))
+                || !$ticket->checkStaffPerm($thisstaff))
             Http::response(404, __('No such ticket'));
 
         include STAFFINC_DIR . 'templates/ticket-preview.tmpl.php';
@@ -177,7 +178,7 @@ class TicketsAjaxAPI extends AjaxController {
         global $thisstaff;
 
         if (!($ticket=Ticket::lookup($tid))
-                || !$ticket->checkStaffAccess($thisstaff))
+                || !$ticket->checkStaffPerm($thisstaff))
             Http::response(404, 'No such ticket');
         elseif (!$bk || !$id)
             Http::response(422, 'Backend and user id required');
@@ -198,7 +199,7 @@ class TicketsAjaxAPI extends AjaxController {
         global $thisstaff;
 
         if (!($ticket=Ticket::lookup($tid))
-                || !$ticket->checkStaffAccess($thisstaff))
+                || !$ticket->checkStaffPerm($thisstaff))
             Http::response(404, __('No such ticket'));
 
 
@@ -248,7 +249,7 @@ class TicketsAjaxAPI extends AjaxController {
         if(!($c=Collaborator::lookup($cid))
                 || !($user=$c->getUser())
                 || !($ticket=$c->getTicket())
-                || !$ticket->checkStaffAccess($thisstaff)
+                || !$ticket->checkStaffPerm($thisstaff)
                 )
             Http::response(404, 'Unknown collaborator');
 
@@ -267,7 +268,7 @@ class TicketsAjaxAPI extends AjaxController {
 
         if(!($collaborator=Collaborator::lookup($cid))
                 || !($ticket=$collaborator->getTicket())
-                || !$ticket->checkStaffAccess($thisstaff))
+                || !$ticket->checkStaffPerm($thisstaff))
             Http::response(404, 'Unknown collaborator');
 
         return self::_collaborator($collaborator);
@@ -277,7 +278,7 @@ class TicketsAjaxAPI extends AjaxController {
         global $thisstaff;
 
         if(!($ticket=Ticket::lookup($tid))
-                || !$ticket->checkStaffAccess($thisstaff))
+                || !$ticket->checkStaffPerm($thisstaff))
             Http::response(404, 'No such ticket');
 
         if($ticket->getCollaborators())
@@ -290,7 +291,7 @@ class TicketsAjaxAPI extends AjaxController {
         global $thisstaff;
 
         if (!($ticket=Ticket::lookup($tid))
-                || !$ticket->checkStaffAccess($thisstaff))
+                || !$ticket->checkStaffPerm($thisstaff))
             Http::response(404, 'No such ticket');
 
         ob_start();
@@ -316,7 +317,7 @@ class TicketsAjaxAPI extends AjaxController {
         global $thisstaff;
 
         if(!($ticket=Ticket::lookup($tid))
-                || !$ticket->checkStaffAccess($thisstaff))
+                || !$ticket->checkStaffPerm($thisstaff))
             Http::response(404, 'No such ticket');
 
         $errors = $info = array();
@@ -362,7 +363,7 @@ class TicketsAjaxAPI extends AjaxController {
 
         if(!$thisstaff
                 || !($ticket=Ticket::lookup($tid))
-                || !$ticket->checkStaffAccess($thisstaff))
+                || !$ticket->checkStaffPerm($thisstaff))
             Http::response(404, 'No such ticket');
 
 
@@ -389,7 +390,7 @@ class TicketsAjaxAPI extends AjaxController {
 
         if(!$thisstaff
                 || !($ticket=Ticket::lookup($tid))
-                || !$ticket->checkStaffAccess($thisstaff)
+                || !$ticket->checkStaffPerm($thisstaff)
                 || !($user = User::lookup($ticket->getOwnerId())))
             Http::response(404, 'No such ticket/user');
 
@@ -416,7 +417,7 @@ class TicketsAjaxAPI extends AjaxController {
 
         if(!$thisstaff
                 || !($ticket=Ticket::lookup($tid))
-                || !$ticket->checkStaffAccess($thisstaff))
+                || !$ticket->checkStaffPerm($thisstaff))
             Http::response(404, 'No such ticket');
 
 
@@ -452,7 +453,7 @@ class TicketsAjaxAPI extends AjaxController {
             Http::response(403, "Login required");
         elseif (!($ticket = Ticket::lookup($ticket_id)))
             Http::response(404, "No such ticket");
-        elseif (!$ticket->checkStaffAccess($thisstaff))
+        elseif (!$ticket->checkStaffPerm($thisstaff))
             Http::response(403, "Access Denied");
         elseif (!isset($_POST['forms']))
             Http::response(422, "Send updated forms list");
@@ -491,7 +492,7 @@ class TicketsAjaxAPI extends AjaxController {
         global $thisstaff, $cfg;
 
         if (!($ticket = Ticket::lookup($tid))
-                || !$ticket->checkStaffAccess($thisstaff))
+                || !$ticket->checkStaffPerm($thisstaff))
             Http::response(404, 'Unknown ticket ID');
 
 
@@ -533,7 +534,7 @@ class TicketsAjaxAPI extends AjaxController {
             Http::response(403, 'Access denied');
         elseif (!$tid
                 || !($ticket=Ticket::lookup($tid))
-                || !$ticket->checkStaffAccess($thisstaff))
+                || !$ticket->checkStaffPerm($thisstaff))
             Http::response(404, 'Unknown ticket #');
 
         $role = $thisstaff->getRole($ticket->getDeptId());
@@ -546,12 +547,12 @@ class TicketsAjaxAPI extends AjaxController {
                 $state = 'open';
                 break;
             case 'close':
-                if (!$role->canCloseTickets())
+                if (!$role->hasPerm(TicketModel::PERM_CLOSE))
                     Http::response(403, 'Access denied');
                 $state = 'closed';
                 break;
             case 'delete':
-                if (!$role->canDeleteTickets())
+                if (!$role->hasPerm(TicketModel::PERM_DELETE))
                     Http::response(403, 'Access denied');
                 $state = 'deleted';
                 break;
@@ -573,7 +574,7 @@ class TicketsAjaxAPI extends AjaxController {
             Http::response(403, 'Access denied');
         elseif (!$tid
                 || !($ticket=Ticket::lookup($tid))
-                || !$ticket->checkStaffAccess($thisstaff))
+                || !$ticket->checkStaffPerm($thisstaff))
             Http::response(404, 'Unknown ticket #');
 
         $errors = $info = array();
@@ -584,23 +585,22 @@ class TicketsAjaxAPI extends AjaxController {
         elseif ($status->getId() == $ticket->getStatusId())
             $errors['err'] = sprintf(__('Ticket already set to %s status'),
                     __($status->getName()));
-        else {
+        elseif (($role = $thisstaff->getRole($ticket->getDeptId()))) {
             // Make sure the agent has permission to set the status
-            $role = $thisstaff->getRole($ticket->getDeptId());
             switch(mb_strtolower($status->getState())) {
                 case 'open':
-                    if (!$role->canCloseTickets()
-                            && !$role->canCreateTickets())
+                    if (!$role->hasPerm(TicketModel::PERM_CLOSE)
+                            && !$role->hasPerm(TicketModel::PERM_CREATE))
                         $errors['err'] = sprintf(__('You do not have permission %s.'),
                                 __('to reopen tickets'));
                     break;
                 case 'closed':
-                    if (!$role->canCloseTickets())
+                    if (!$role->hasPerm(TicketModel::PERM_CLOSE))
                         $errors['err'] = sprintf(__('You do not have permission %s.'),
                                 __('to resolve/close tickets'));
                     break;
                 case 'deleted':
-                    if (!$role->canDeleteTickets())
+                    if (!$role->hasPerm(TicketModel::PERM_DELETE))
                         $errors['err'] = sprintf(__('You do not have permission %s.'),
                                 __('to archive/delete tickets'));
                     break;
@@ -608,6 +608,8 @@ class TicketsAjaxAPI extends AjaxController {
                     $errors['err'] = sprintf('%s %s',
                             __('Unknown or invalid'), __('status'));
             }
+        } else {
+            $errors['err'] = __('Access denied');
         }
 
         $state = strtolower($status->getState());
@@ -658,12 +660,12 @@ class TicketsAjaxAPI extends AjaxController {
                 $state = 'open';
                 break;
             case 'close':
-                if (!$thisstaff->canCloseTickets())
+                if (!$thisstaff->hasPerm(TicketModel::PERM_CLOSE))
                     Http::response(403, 'Access denied');
                 $state = 'closed';
                 break;
             case 'delete':
-                if (!$thisstaff->canDeleteTickets())
+                if (!$thisstaff->hasPerm(TicketModel::PERM_DELETE))
                     Http::response(403, 'Access denied');
 
                 $state = 'deleted';
@@ -697,18 +699,18 @@ class TicketsAjaxAPI extends AjaxController {
             // Make sure the agent has permission to set the status
             switch(mb_strtolower($status->getState())) {
                 case 'open':
-                    if (!$thisstaff->canCloseTickets()
-                            && !$thisstaff->canCreateTickets())
+                    if (!$thisstaff->hasPerm(TicketModel::PERM_CLOSE)
+                            && !$thisstaff->hasPerm(TicketModel::PERM_CREATE))
                         $errors['err'] = sprintf(__('You do not have permission %s.'),
                                 __('to reopen tickets'));
                     break;
                 case 'closed':
-                    if (!$thisstaff->canCloseTickets())
+                    if (!$thisstaff->hasPerm(TicketModel::PERM_CLOSE))
                         $errors['err'] = sprintf(__('You do not have permission %s.'),
                                 __('to resolve/close tickets'));
                     break;
                 case 'deleted':
-                    if (!$thisstaff->canDeleteTickets())
+                    if (!$thisstaff->hasPerm(TicketModel::PERM_DELETE))
                         $errors['err'] = sprintf(__('You do not have permission %s.'),
                                 __('to archive/delete tickets'));
                     break;
@@ -723,9 +725,10 @@ class TicketsAjaxAPI extends AjaxController {
             $i = 0;
             $comments = $_REQUEST['comments'];
             foreach ($_REQUEST['tids'] as $tid) {
+
                 if (($ticket=Ticket::lookup($tid))
                         && $ticket->getStatusId() != $status->getId()
-                        && $ticket->checkStaffAccess($thisstaff)
+                        && $ticket->checkStaffPerm($thisstaff)
                         && $ticket->setStatus($status, $comments, $errors))
                     $i++;
             }
@@ -859,7 +862,7 @@ class TicketsAjaxAPI extends AjaxController {
         global $thisstaff;
 
         if (!($ticket=Ticket::lookup($tid))
-                || !$ticket->checkStaffAccess($thisstaff))
+                || !$ticket->checkStaffPerm($thisstaff))
             Http::response(404, 'Unknown ticket');
 
          include STAFFINC_DIR . 'ticket-tasks.inc.php';
@@ -868,10 +871,12 @@ class TicketsAjaxAPI extends AjaxController {
     function addTask($tid) {
         global $thisstaff;
 
-        if (!($ticket=Ticket::lookup($tid))
-                || !$ticket->checkStaffAccess($thisstaff))
+        if (!($ticket=Ticket::lookup($tid)))
             Http::response(404, 'Unknown ticket');
 
+        if (!$ticket->checkStaffPerm($thisstaff, Task::PERM_CREATE))
+            Http::response(403, 'Permission denied');
+
         $info=$errors=array();
 
         if ($_POST) {
diff --git a/include/ajax.users.php b/include/ajax.users.php
index 02a3b1668427dd011db3b9edba9c6b063e584dd2..cca1b11631263981e16c4f61b4ae6f5065878e45 100644
--- a/include/ajax.users.php
+++ b/include/ajax.users.php
@@ -206,7 +206,7 @@ class UsersAjaxAPI extends AjaxController {
         $info = array();
         if ($_POST) {
             if ($user->tickets->count()) {
-                if (!$thisstaff->canDeleteTickets()) {
+                if (!$thisstaff->hasPerm(TicketModel::PERM_DELETE)) {
                     $info['error'] = __('You do not have permission to delete a user with tickets!');
                 } elseif ($_POST['deletetickets']) {
                     foreach($user->tickets as $ticket)
diff --git a/include/class.canned.php b/include/class.canned.php
index a7154b974ef56d877c1bead681c8cb9fd4e081f2..ca1354332107d391b4f55a512fd5a3a149d8a117 100644
--- a/include/class.canned.php
+++ b/include/class.canned.php
@@ -15,6 +15,25 @@
 **********************************************************************/
 include_once(INCLUDE_DIR.'class.file.php');
 
+class CannedModel {
+
+    const PERM_MANAGE = 'canned.manage';
+
+    static protected $perms = array(
+            self::PERM_MANAGE => array(
+                'title' =>
+                /* @trans */ 'Premade',
+                'desc'  =>
+                /* @trans */ 'Ability to add/update/disable/delete canned responses')
+    );
+
+    static function getPermissions() {
+        return self::$perms;
+    }
+}
+
+RolePermission::register( /* @trans */ 'Knowledgebase', CannedModel::getPermissions());
+
 class Canned {
     var $id;
     var $ht;
diff --git a/include/class.email.php b/include/class.email.php
index 53a966d71e23e8f5f6f5d01c09b570247c1d3948..ab0727f7cbf4c5cf941ebbf1893fc6873f6eb79f 100644
--- a/include/class.email.php
+++ b/include/class.email.php
@@ -11,6 +11,7 @@
 
     vim: expandtab sw=4 ts=4 sts=4:
 **********************************************************************/
+include_once INCLUDE_DIR.'class.role.php';
 include_once(INCLUDE_DIR.'class.dept.php');
 include_once(INCLUDE_DIR.'class.mailfetch.php');
 
@@ -34,6 +35,16 @@ class EmailModel extends VerySimpleModel {
         )
     );
 
+    const PERM_BANLIST = 'emails.banlist';
+
+    static protected $perms = array(
+            self::PERM_BANLIST => array(
+                'title' =>
+                /* @trans */ 'Banlist',
+                'desc'  =>
+                /* @trans */ 'Ability to add/remove emails from banlist via ticket interface'),
+            );
+
     function getId() {
         return $this->email_id;
     }
@@ -44,8 +55,14 @@ class EmailModel extends VerySimpleModel {
 
         return $this->email;
     }
+
+    static function getPermissions() {
+        return self::$perms;
+    }
 }
 
+RolePermission::register(/* @trans */ 'Miscellaneous', EmailModel::getPermissions());
+
 class Email {
     var $id;
     var $address;
diff --git a/include/class.faq.php b/include/class.faq.php
index f4bb27b0081f2a6d68d8a992a8233b58bec2b211..9f4b3030577e3b2fa0bb45148e26c7bdae0aca89 100644
--- a/include/class.faq.php
+++ b/include/class.faq.php
@@ -46,6 +46,16 @@ class FAQ extends VerySimpleModel {
         ),
     );
 
+    const PERM_MANAGE  = 'faq.manage';
+    static protected $perms = array(
+            self::PERM_MANAGE => array(
+                'title' =>
+                /* @trans */ 'FAQ',
+                'desc'  =>
+                /* @trans */ 'Ability to add/update/disable/delete knowledgebase categories and FAQs'),
+            );
+
+
     var $attachments;
     var $topics;
     var $_local;
@@ -480,8 +490,14 @@ class FAQ extends VerySimpleModel {
             $this->updated = SqlFunction::NOW();
         return parent::save($refetch || $this->dirty);
     }
+
+    static function getPermissions() {
+        return self::$perms;
+    }
 }
-FAQ::_inspect();
+
+RolePermission::register( /* @trans */ 'Knowledgebase',
+        FAQ::getPermissions());
 
 class FaqTopic extends VerySimpleModel {
 
diff --git a/include/class.knowledgebase.php b/include/class.knowledgebase.php
index 9ee15bdac03c431489c55adad7b800884b0b8a66..33b214d6a1cbc21d293cfa0cfffd4ff20c735b9f 100644
--- a/include/class.knowledgebase.php
+++ b/include/class.knowledgebase.php
@@ -35,7 +35,7 @@ class Knowledgebase {
     /* ------------------> Getter methods <--------------------- */
     function getTitle() { return $this->title; }
     function isEnabled() { return !!$this->enabled; }
-    function getAnswer() { 
+    function getAnswer() {
         if (!isset($this->answer)) {
             if ($res=db_query('SELECT answer FROM '.CANNED_TABLE
                     .' WHERE canned_id='.db_input($this->id))) {
@@ -92,14 +92,14 @@ class Knowledgebase {
     }
 
     /* -------------> Database access methods <----------------- */
-    function update() { 
+    function update() {
         if (!@$this->validate()) return false;
         db_query(
             'UPDATE '.CANNED_TABLE.' SET title='.db_input($this->title)
                 .', isenabled='.db_input($this->enabled)
                 .', dept_id='.db_input($this->department)
                 .', updated=NOW()'
-                .((isset($this->answer)) 
+                .((isset($this->answer))
                     ? ', answer='.db_input($this->answer) : '')
                 .' WHERE canned_id='.db_input($this->id));
         return db_affected_rows() == 1;
diff --git a/include/class.nav.php b/include/class.nav.php
index 375d5254f2cbb48ce121dce8639dc392ff0e5fdc..e8359e00d44eddd282488283ab6f1c524fda6006 100644
--- a/include/class.nav.php
+++ b/include/class.nav.php
@@ -140,7 +140,7 @@ class StaffNav {
                                             'iconclass'=>'assignedTickets',
                                             'droponly'=>true);
 
-                        if($staff->canCreateTickets())
+                        if ($staff->hasPerm(TicketModel::PERM_CREATE))
                             $subnav[]=array('desc'=>__('New Ticket'),
                                             'title' => __('Open a New Ticket'),
                                             'href'=>'tickets.php?a=open',
@@ -161,9 +161,9 @@ class StaffNav {
                 case 'kbase':
                     $subnav[]=array('desc'=>__('FAQs'),'href'=>'kb.php', 'urls'=>array('faq.php'), 'iconclass'=>'kb');
                     if($staff) {
-                        if($staff->getRole()->canManageFAQ())
+                        if ($staff->getRole()->hasPerm(FAQ::PERM_MANAGE))
                             $subnav[]=array('desc'=>__('Categories'),'href'=>'categories.php','iconclass'=>'faq-categories');
-                        if($staff->getRole()->canManageCannedResponses())
+                        if ($staff->getRole()->hasPerm(CannedModel::PERM_MANAGE))
                             $subnav[]=array('desc'=>__('Canned Responses'),'href'=>'canned.php','iconclass'=>'canned');
                     }
                    break;
diff --git a/include/class.organization.php b/include/class.organization.php
index 0c6bb5212d2cd734091ad26c9c473d5deeda4b9f..fa0ffc9cf568bc85ee20eb8e6e2726bba8f1ac81 100644
--- a/include/class.organization.php
+++ b/include/class.organization.php
@@ -466,5 +466,4 @@ Filter::addSupportedMatches(/*@trans*/ 'Organization Data', function() {
     }
     return $matches;
 },40);
-Organization::_inspect();
 ?>
diff --git a/include/class.report.php b/include/class.report.php
new file mode 100644
index 0000000000000000000000000000000000000000..0e8bae980f7af555cd743551655df3fbdb89e7e5
--- /dev/null
+++ b/include/class.report.php
@@ -0,0 +1,21 @@
+<?php
+
+class ReportModel {
+
+    const PERM_AGENTS = 'stats.agents';
+
+    static protected $perms = array(
+            self::PERM_AGENTS => array(
+                'title' =>
+                /* @trans */ 'Stats',
+                'desc'  =>
+                /* @trans */ 'Ability to view stats of other agents in allowed departments'),
+            );
+
+    static function getPermissions() {
+        return self::$perms;
+    }
+}
+
+RolePermission::register(/* @trans */ 'Miscellaneous', ReportModel::getPermissions());
+?>
diff --git a/include/class.role.php b/include/class.role.php
index 38fbc68a93465250611c7165a9c7e842ca5b207e..476bd9a72b89721f1235a9f3b1dfdf65a2d89587 100644
--- a/include/class.role.php
+++ b/include/class.role.php
@@ -81,6 +81,10 @@ class Role extends RoleModel {
 
     var $_perm;
 
+    function hasPerm($perm) {
+        return $this->getPermission()->has($perm);
+    }
+
     function getPermission() {
         if (!$this->_perm) {
             $this->_perm = new RolePermission(
@@ -257,73 +261,17 @@ class Role extends RoleModel {
 
 class RolePermission {
 
-    static $_permissions = array(
-            /* @trans */ 'Tickets' => array(
-                'ticket.create'  => array(
-                    /* @trans */ 'Create',
-                    /* @trans */ 'Ability to open tickets on behalf of users'),
-                'ticket.edit'   => array(
-                    /* @trans */ 'Edit',
-                    /* @trans */ 'Ability to edit tickets'),
-                'ticket.assign'   => array(
-                    /* @trans */ 'Assign',
-                    /* @trans */ 'Ability to assign tickets to agents or teams'),
-                'ticket.transfer'   => array(
-                    /* @trans */ 'Transfer',
-                    /* @trans */ 'Ability to transfer tickets between departments'),
-                'ticket.reply'  => array(
-                    /* @trans */ 'Post Reply',
-                    /* @trans */ 'Ability to post a ticket reply'),
-                'ticket.close'   => array(
-                    /* @trans */ 'Close',
-                    /* @trans */ 'Ability to close tickets'),
-                'ticket.delete'   => array(
-                    /* @trans */ 'Delete',
-                    /* @trans */ 'Ability to delete tickets'),
-                ),
-            /* @trans */ 'Tasks' => array(
-                'task.create'  => array(
-                    /* @trans */ 'Create',
-                    /* @trans */ 'Ability to create tasks'),
-                'task.edit'   => array(
-                    /* @trans */ 'Edit',
-                    /* @trans */ 'Ability to edit tasks'),
-                'task.assign'   => array(
-                    /* @trans */ 'Assign',
-                    /* @trans */ 'Ability to assign tasks to agents or teams'),
-                'task.transfer'   => array(
-                    /* @trans */ 'Transfer',
-                    /* @trans */ 'Ability to transfer tasks between departments'),
-                'task.close'   => array(
-                    /* @trans */ 'Close',
-                    /* @trans */ 'Ability to close tasks'),
-                'task.delete'   => array(
-                    /* @trans */ 'Delete',
-                    /* @trans */ 'Ability to delete tasks'),
-                ),
-            /* @trans */ 'Knowledgebase' => array(
-                'kb.premade'   => array(
-                    /* @trans */ 'Premade',
-                    /* @trans */ 'Ability to add/update/disable/delete canned responses'),
-                'kb.faq'   => array(
-                    /* @trans */ 'FAQ',
-                    /* @trans */ 'Ability to add/update/disable/delete knowledgebase categories and FAQs'),
-                ),
-            /* @trans */ 'Misc.' => array(
-                'stats.agents'   => array(
-                    /* @trans */ 'Stats',
-                    /* @trans */ 'Ability to view stats of other agents in allowed departments'),
-                'emails.banlist'   => array(
-                    /* @trans */ 'Banlist',
-                    /* @trans */ 'Ability to add/remove emails from banlist via ticket interface'),
-                ),
+    // Predefined groups are for sort order.
+    // New groups will be appended to the bottom
+    static protected $_permissions = array(
+            /* @trans */ 'Tickets' => array(),
+            /* @trans */ 'Tasks' => array(),
+            /* @trans */ 'Knowledgebase' => array(),
+            /* @trans */ 'Miscellaneous' => array(),
             );
 
     var $perms;
 
-    static function allPermissions() {
-        return static::$_permissions;
-    }
 
     function __construct($perms) {
         $this->perms = $perms;
@@ -356,89 +304,13 @@ class RolePermission {
         return $this->perms;
     }
 
-    /* tickets */
-    function canCreateTickets() {
-        return ($this->has('ticket.create'));
-    }
-
-    function canEditTickets() {
-        return ($this->has('ticket.edit'));
-    }
-
-    function canAssignTickets() {
-        return ($this->has('ticket.assign'));
-    }
-
-    function canTransferTickets() {
-        return ($this->has('ticket.transfer'));
-    }
-
-    function canPostTicketReply() {
-        return ($this->has('ticket.reply'));
-    }
-
-    function canCloseTickets() {
-        return ($this->has('ticket.close'));
-    }
-
-    function canDeleteTickets() {
-        return ($this->has('ticket.delete'));
-    }
-
-    /* tasks */
-    function canCreateTasks() {
-        return ($this->get('task.create'));
-    }
-
-    function canEditTasks() {
-        return ($this->get('task.edit'));
-    }
-
-    function canAssignTasks() {
-        return ($this->get('task.assign'));
-    }
-
-    function canTransferTasks() {
-        return ($this->get('task.transfer'));
-    }
-
-    function canPostTaskReply() {
-        return ($this->get('task.reply'));
-    }
-
-    function canCloseTasks() {
-        return ($this->get('task.close'));
-    }
-
-    function canDeleteTasks() {
-        return ($this->get('task.delete'));
-    }
-
-    /* Knowledge base */
-    function canManagePremade() {
-        return ($this->has('kb.premade'));
-    }
-
-    function canManageCannedResponses() {
-        return ($this->canManagePremade());
-    }
-
-    function canManageFAQ() {
-        return ($this->has('kb.faq'));
-    }
-
-    function canManageFAQs() {
-        return ($this->canManageFAQ());
-    }
-
-    /* stats */
-    function canViewStaffStats() {
-        return ($this->has('stats.agents'));
+    static function allPermissions() {
+        return static::$_permissions;
     }
 
-    /* email */
-    function canBanEmails() {
-        return ($this->has('emails.banlist'));
+    static function register($group, $perms) {
+        static::$_permissions[$group] = array_merge(
+                            static::$_permissions[$group] ?: array(), $perms);
     }
 }
 ?>
diff --git a/include/class.staff.php b/include/class.staff.php
index a6aecc63e8328d79e66d517e09485ba728b36ce4..9ffd6bdeaf8a3ac738eb8fcb61ac5aa662aa942a 100644
--- a/include/class.staff.php
+++ b/include/class.staff.php
@@ -309,7 +309,7 @@ implements AuthenticatedUser {
         return $this->role;
     }
 
-    function hasPermission($perm) {
+    function hasPerm($perm) {
         if (!isset($this->_perms)) {
             foreach ($this->getDepartments() as $deptId) {
                 if (($role = $this->getRole($deptId))) {
@@ -322,26 +322,10 @@ implements AuthenticatedUser {
         return @$this->_perms[$perm];
     }
 
-    function canCreateTickets() {
-        return $this->hasPermission('ticket.create');
-    }
-
-    function canAssignTickets() {
-        return $this->hasPermission('ticket.create');
-    }
-
-    function canCloseTickets() {
-        return $this->hasPermission('ticket.close');
-    }
-
-    function canDeleteTickets() {
-        return $this->hasPermission('ticket.delete');
-    }
-
     function canManageTickets() {
         return ($this->isAdmin()
-                || $this->canDeleteTickets()
-                || $this->canCloseTickets());
+                || $this->hasPerm(TicketModel::PERM_DELETE)
+                || $this->hasPerm(TicketModel::PERM_CLOSE));
     }
 
     function isManager() {
diff --git a/include/class.task.php b/include/class.task.php
index 9b155e249a94b9b20ebf89295b76b23e26d05485..54da8170f882a43bc4d4935835be8ee505f051b6 100644
--- a/include/class.task.php
+++ b/include/class.task.php
@@ -1,4 +1,21 @@
 <?php
+/*********************************************************************
+    class.task.php
+
+    Task
+
+    Peter Rotich <peter@osticket.com>
+    Copyright (c)  2014 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:
+**********************************************************************/
+
+include_once INCLUDE_DIR.'class.role.php';
+
 
 class TaskModel extends VerySimpleModel {
     static $meta = array(
@@ -15,6 +32,46 @@ class TaskModel extends VerySimpleModel {
         ),
     );
 
+    const PERM_CREATE   = 'task.create';
+    const PERM_EDIT     = 'task.edit';
+    const PERM_ASSIGN   = 'task.assign';
+    const PERM_TRANSFER = 'task.transfer';
+    const PERM_CLOSE    = 'task.close';
+    const PERM_DELETE   = 'task.delete';
+
+    static protected $perms = array(
+            self::PERM_CREATE    => array(
+                'title' =>
+                /* @trans */ 'Create',
+                'desc'  =>
+                /* @trans */ 'Ability to create tasks'),
+            self::PERM_EDIT      => array(
+                'title' =>
+                /* @trans */ 'Edit',
+                'desc'  =>
+                /* @trans */ 'Ability to edit tasks'),
+            self::PERM_ASSIGN    => array(
+                'title' =>
+                /* @trans */ 'Assign',
+                'desc'  =>
+                /* @trans */ 'Ability to assign tasks to agents or teams'),
+            self::PERM_TRANSFER  => array(
+                'title' =>
+                /* @trans */ 'Transfer',
+                'desc'  =>
+                /* @trans */ 'Ability to transfer tasks between departments'),
+            self::PERM_CLOSE     => array(
+                'title' =>
+                /* @trans */ 'Close',
+                'desc'  =>
+                /* @trans */ 'Ability to close tasks'),
+            self::PERM_DELETE    => array(
+                'title' =>
+                /* @trans */ 'Delete',
+                'desc'  =>
+                /* @trans */ 'Ability to delete tasks'),
+            );
+
     const ISOPEN    = 0x0001;
     const ISOVERDUE = 0x0002;
 
@@ -83,8 +140,15 @@ class TaskModel extends VerySimpleModel {
         return $this->hasFlag(self::ISOVERDUE);
     }
 
+    static function getPermissions() {
+        return self::$perms;
+    }
+
 }
 
+RolePermission::register(/* @trans */ 'Tasks', TaskModel::getPermissions());
+
+
 class Task extends TaskModel {
     var $form;
     var $entry;
@@ -101,6 +165,32 @@ class Task extends TaskModel {
         return $this->__cdata('title', ObjectModel::OBJECT_TYPE_TASK);
     }
 
+    function checkStaffPerm($staff, $perm=null) {
+
+        // Must be a valid staff
+        if (!$staff instanceof Staff && !($staff=Staff::lookup($staff)))
+            return false;
+
+        // Check access based on department or assignment
+        if (!$staff->canAccessDept($this->getDeptId())
+                && $this->isOpen()
+                && $staff->getId() != $this->getStaffId()
+                && !$staff->isTeamMember($this->getTeamId()))
+            return false;
+
+        // At this point staff has access unless a specific permission is
+        // requested
+        if ($perm === null)
+            return true;
+
+        // Permission check requested -- get role.
+        if (!($role=$staff->getRole($this->getDeptId())))
+            return false;
+
+        // Check permission based on the effective role
+        return $role->hasPerm($perm);
+    }
+
     function getAssignees() {
 
         $assignees=array();
diff --git a/include/class.ticket.php b/include/class.ticket.php
index 010a52e5e258231e57ba35b53cdd341321f9a23e..b1b3ea30cddb714508d9cca00235dd7c4225eda1 100644
--- a/include/class.ticket.php
+++ b/include/class.ticket.php
@@ -86,6 +86,54 @@ class TicketModel extends VerySimpleModel {
         )
     );
 
+    const PERM_CREATE   = 'ticket.create';
+    const PERM_EDIT     = 'ticket.edit';
+    const PERM_ASSIGN   = 'ticket.assign';
+    const PERM_TRANSFER = 'ticket.transfer';
+    const PERM_REPLY    = 'ticket.reply';
+    const PERM_CLOSE    = 'ticket.close';
+    const PERM_DELETE   = 'ticket.delete';
+
+
+    static protected $perms = array(
+            self::PERM_CREATE => array(
+                'title' =>
+                /* @trans */ 'Create',
+                'desc'  =>
+                /* @trans */ 'Ability to open tickets on behalf of users'),
+            self::PERM_EDIT => array(
+                'title' =>
+                /* @trans */ 'Edit',
+                'desc'  =>
+                /* @trans */ 'Ability to edit tickets'),
+            self::PERM_ASSIGN => array(
+                'title' =>
+                /* @trans */ 'Assign',
+                'desc'  =>
+                /* @trans */ 'Ability to assign tickets to agents or teams'),
+            self::PERM_TRANSFER => array(
+
+                'title' =>
+                /* @trans */ 'Transfer',
+                'desc'  =>
+                /* @trans */ 'Ability to transfer tickets between departments'),
+            self::PERM_REPLY => array(
+                'title' =>
+                /* @trans */ 'Post Reply',
+                'desc'  =>
+                /* @trans */ 'Ability to post a ticket reply'),
+            self::PERM_CLOSE => array(
+                'title' =>
+                /* @trans */ 'Close',
+                'desc'  =>
+                /* @trans */ 'Ability to close tickets'),
+            self::PERM_DELETE => array(
+                'title' =>
+                /* @trans */ 'Delete',
+                'desc'  =>
+                /* @trans */ 'Ability to delete tickets'),
+            );
+
     function getId() {
         return $this->ticket_id;
     }
@@ -143,8 +191,14 @@ EOF;
                 static::$meta->processJoin(static::$meta['joins']['cdata+'.$form->id]);
         }
     }
+
+    static function getPermissions() {
+        return self::$perms;
+    }
 }
 
+RolePermission::register(/* @trans */ 'Tickets', TicketModel::getPermissions());
+
 class TicketCData extends VerySimpleModel {
     static $meta = array(
         'pk' => array('ticket_id'),
@@ -301,29 +355,32 @@ class Ticket {
         return null !== $this->getLock();
     }
 
-    function checkStaffAccess($staff) {
+    function checkStaffPerm($staff, $perm=null) {
 
-        if(!is_object($staff) && !($staff=Staff::lookup($staff)))
+        // Must be a valid staff
+        if (!$staff instanceof Staff && !($staff=Staff::lookup($staff)))
             return false;
 
-        // Staff has access to the department.
-        if (!$staff->showAssignedOnly()
-                && $staff->canAccessDept($this->getDeptId()))
-            return true;
-
-        // Only consider assignment if the ticket is open
-        if (!$this->isOpen())
+        // 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()))
             return false;
 
-        // Check ticket access based on direct or team assignment
-        if ($staff->getId() == $this->getStaffId()
-                || ($this->getTeamId()
-                    && $staff->isTeamMember($this->getTeamId())
-        ))
+        // At this point staff has view access unless a specific permission is
+        // requested
+        if ($perm === null)
             return true;
 
-        // No access bro!
-        return false;
+        // Permission check requested -- get role.
+        if (!($role=$staff->getRole($this->getDeptId())))
+            return false;
+
+        // Check permission based on the effective role
+        return $role->hasPerm($perm);
     }
 
     function checkUserAccess($user) {
@@ -1030,12 +1087,12 @@ class Ticket {
         // Double check permissions
         switch ($status->getState()) {
         case 'closed':
-            if (!($role->canCloseTickets()))
+            if (!($role->hasPerm(TicketModel::PERM_CLOSE)))
                 return false;
             break;
         case 'deleted':
             // XXX: intercept deleted status and do hard delete
-            if ($role->canDeleteTickets())
+            if ($role->hasPerm(TicketModel::PERM_DELETE))
                 return $this->delete($comments);
             // Agent doesn't have permission to delete  tickets
             return false;
@@ -1654,9 +1711,7 @@ class Ticket {
 
         global $cfg, $thisstaff;
 
-        if(!$thisstaff
-                || !($role=$thisstaff->getRole($this->getDeptId()))
-                || !$role->canTransferTickets())
+        if (!$this->checkStaffPerm($thisstaff, TicketModel::PERM_TRANSFER))
             return false;
 
         $currentDept = $this->getDeptName(); //Current department
@@ -1808,8 +1863,8 @@ class Ticket {
 
         if (!$user
                 || ($user->getId() == $this->getOwnerId())
-                || !($role=$thisstaff->getRole($this->getDeptId()))
-                || !$role->canEditTickets())
+                || !($this->checkStaffPerm($thisstaff,
+                        TicketModel::PERM_EDIT)))
             return false;
 
         $sql ='UPDATE '.TICKET_TABLE.' SET updated = NOW() '
@@ -2216,7 +2271,7 @@ class Ticket {
                         // No need to alert the poster!
                         || $note->getStaffId() == $staff->getId()
                         // Make sure staff has access to ticket
-                        || ($isClosed && !$this->checkStaffAccess($staff))
+                        || ($isClosed && !$this->checkStaffPerm($staff))
                         )
                     continue;
                 $alert = $this->replaceVars($msg, array('recipient' => $staff));
@@ -2290,9 +2345,8 @@ class Ticket {
         global $cfg, $thisstaff;
 
         if (!$cfg
-                || !$thisstaff
-                || !($role=$thisstaff->getRole($this->getDeptId()))
-                || !$role->canEditTickets())
+                || !($this->checkStaffPerm($thisstaff,
+                        TicketModel::PERM_EDIT)))
             return false;
 
         $fields=array();
@@ -3001,7 +3055,8 @@ class Ticket {
     static function open($vars, &$errors) {
         global $thisstaff, $cfg;
 
-        if (!$thisstaff || !$thisstaff->canCreateTickets()) return false;
+        if (!$thisstaff || !$thisstaff->hasPerm(TicketModel::PERM_CREATE))
+            return false;
 
         if($vars['source'] && !in_array(strtolower($vars['source']),array('email','phone','other')))
             $errors['source']=sprintf(__('Invalid source given - %s'),Format::htmlchars($vars['source']));
@@ -3015,7 +3070,7 @@ class Ticket {
                 $errors['name'] = __('Name is required');
         }
 
-        if (!$thisstaff->canAssignTickets())
+        if (!$thisstaff->hasPerm(TicketModel::PERM_ASSIGN))
             unset($vars['assignId']);
 
         //TODO: Deny action based on selected department.
@@ -3036,14 +3091,15 @@ class Ticket {
 
         // post response - if any
         $response = null;
-        if($vars['response'] && $role->canPostTicketReply()) {
+        if($vars['response'] && $role->hasPerm(TicketModel::PERM_REPLY)) {
 
             $vars['response'] = $ticket->replaceVars($vars['response']);
             // $vars['cannedatachments'] contains the attachments placed on
             // the response form.
             if(($response=$ticket->postReply($vars, $errors, false))) {
                 //Only state supported is closed on response
-                if(isset($vars['ticket_state']) && $role->canCloseTickets())
+                if(isset($vars['ticket_state']) &&
+                        $role->hasPerm(TicketModel::PERM_CLOSE))
                     $ticket->setState($vars['ticket_state']);
             }
         }
diff --git a/include/staff/category.inc.php b/include/staff/category.inc.php
index 0aa2bbf99d3fe558f964674882c16c3b3fd92723..55926a0aaef1cf2a716cad851466a93da2f58c11 100644
--- a/include/staff/category.inc.php
+++ b/include/staff/category.inc.php
@@ -1,5 +1,8 @@
 <?php
-if(!defined('OSTSCPINC') || !$thisstaff || !$thisstaff->getRole()->canManageFAQ()) die('Access Denied');
+if (!defined('OSTSCPINC') || !$thisstaff
+        || !$thisstaff->getRole()->hasPerm(FAQ::PERM_MANAGE))
+    die('Access Denied');
+
 $info=array();
 $qstr='';
 if($category && $_REQUEST['a']!='add'){
diff --git a/include/staff/faq-category.inc.php b/include/staff/faq-category.inc.php
index f4edf2d9a1f33f088e5d8c48ce23890deeefcaa1..afc5194bb7c08ebfe4ea4caeed74072a5b434db4 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()->canManageFAQ()) {
+if ($thisstaff->getRole()->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 2c20b6dcb793727089e49db063fa4afab3417fed..8afda9a67e320f6636f9fe4119c011561ea35a58 100644
--- a/include/staff/faq-view.inc.php
+++ b/include/staff/faq-view.inc.php
@@ -79,7 +79,7 @@ $query = http_build_query($query); ?>
         echo __('Print'); ?>
     </a></button>
 <?php
-if ($thisstaff->getRole()->canManageFAQ()) { ?>
+if ($thisstaff->getRole()->hasPerm(FAQ::PERM_MANAGE)) { ?>
     <button>
     <i class="icon-edit"></i>
     <a href="faq.php?id=<?php echo $faq->getId(); ?>&a=edit"><?php
@@ -104,7 +104,7 @@ if ($thisstaff->getRole()->canManageFAQ()) { ?>
 <hr>
 
 <?php
-if ($thisstaff->getRole()->canManageFAQ()) { ?>
+if ($thisstaff->getRole()->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 01b404b3fc6115970a46e332797164635813b618..a1bdfcc7ac36558974060b1bb7cb902dd7311e6f 100644
--- a/include/staff/faq.inc.php
+++ b/include/staff/faq.inc.php
@@ -1,5 +1,8 @@
 <?php
-if(!defined('OSTSCPINC') || !$thisstaff || !$thisstaff->getRole()->canManageFAQ()) die('Access Denied');
+if (!defined('OSTSCPINC') || !$thisstaff
+        || !$thisstaff->getRole()->hasPerm(FAQ::PERM_MANAGE))
+    die('Access Denied');
+
 $info=array();
 $qstr='';
 if($faq){
diff --git a/include/staff/role.inc.php b/include/staff/role.inc.php
index e36b87073bd0132507967d1b11a822f931ac12d4..25384cd39977d15950297605ec05bc11b3308db8 100644
--- a/include/staff/role.inc.php
+++ b/include/staff/role.inc.php
@@ -98,8 +98,8 @@ $info = Format::htmlchars(($errors && $_POST) ? array_merge($info, $_POST) : $in
               &nbsp;&nbsp;
               <?php
                 echo sprintf('%s - <em>%s</em>',
-                      Format::htmlchars(__($v[0])),
-                    Format::htmlchars(__($v[1])));
+                      Format::htmlchars(__($v['title'])),
+                    Format::htmlchars(__($v['desc'])));
               ?>
              </label>
             </td>
diff --git a/include/staff/templates/status-options.tmpl.php b/include/staff/templates/status-options.tmpl.php
index c82696e6ff2e052dd84294afb6586233ae7fa5b8..e12960def51b8ef35e31f6b5e1ff3b76bb83f0b4 100644
--- a/include/staff/templates/status-options.tmpl.php
+++ b/include/staff/templates/status-options.tmpl.php
@@ -14,7 +14,8 @@ $actions= array(
         );
 
 $states = array('open');
-if ($thisstaff->canCloseTickets() && (!$ticket || !$ticket->getMissingRequiredFields()))
+if ($thisstaff->hasPerm(TicketModel::PERM_CLOSE)
+        && (!$ticket || !$ticket->getMissingRequiredFields()))
     $states = array_merge($states, array('closed'));
 
 $statusId = $ticket ? $ticket->getStatusId() : 0;
diff --git a/include/staff/templates/task-view.tmpl.php b/include/staff/templates/task-view.tmpl.php
index 217afdc8c20edd5e7652d7d1b5f9be75fd2db96d..6020b7a3eadfbec200c2f88ef25fff82ba010340 100644
--- a/include/staff/templates/task-view.tmpl.php
+++ b/include/staff/templates/task-view.tmpl.php
@@ -1,35 +1,45 @@
 <?php
-if (!defined('OSTSCPINC') || !$thisstaff || !is_object($task))
+if (!defined('OSTSCPINC')
+        || !$thisstaff
+        || !$task
+        || !($role = $thisstaff->getRole($task->getId())))
     die('Invalid path');
 
-//Make sure the staff is allowed to access this task
-/*
- if (!@$thisstaff->isStaff() || !$task->checkStaffAccess($thisstaff))
-    die('Access Denied');
-*/
 
 $actions = array();
-$actions += array(
-        'edit' => array(
-            'icon' => 'icon-edit',
-            'dialog' => '{"size":"large"}',
-            'action' => __('Edit')
-        ));
-$actions += array(
-        'assign' => array(
-            'icon' => 'icon-user',
-            'action' => $task->isAssigned() ? __('Reassign') : __('Assign')
-        ));
-$actions += array(
-        'transfer' => array(
-            'icon' => 'icon-share',
-            'action' => __('Transfer')
-        ));
-$actions += array(
-        'delete' => array(
-            'icon' => 'icon-trash',
-            'action' => __('Delete')
-        ));
+
+if ($role->hasPerm(Task::PERM_EDIT)) {
+    $actions += array(
+            'edit' => array(
+                'icon' => 'icon-edit',
+                'dialog' => '{"size":"large"}',
+                'action' => __('Edit')
+            ));
+}
+
+if ($role->hasPerm(Task::PERM_ASSIGN)) {
+    $actions += array(
+            'assign' => array(
+                'icon' => 'icon-user',
+                'action' => $task->isAssigned() ? __('Reassign') : __('Assign')
+            ));
+}
+
+if ($role->hasPerm(Task::PERM_TRANSFER)) {
+    $actions += array(
+            'transfer' => array(
+                'icon' => 'icon-share',
+                'action' => __('Transfer')
+            ));
+}
+
+if ($role->hasPerm(Task::PERM_DELETE)) {
+    $actions += array(
+            'delete' => array(
+                'icon' => 'icon-trash',
+                'action' => __('Delete')
+            ));
+}
 
 
 $info=($_POST && $errors)?Format::input($_POST):array();
diff --git a/include/staff/templates/ticket-preview.tmpl.php b/include/staff/templates/ticket-preview.tmpl.php
index 05b78747f9b9f76ec699c965f3eaad2863689beb..875c9d701bdcd641540b6f1c5d37e76b674ddc4c 100644
--- a/include/staff/templates/ticket-preview.tmpl.php
+++ b/include/staff/templates/ticket-preview.tmpl.php
@@ -156,15 +156,15 @@ if($ticket->getNumNotes())
 if($ticket->isOpen())
     $options[]=array('action'=>__('Reply'),'url'=>"tickets.php?id=$tid#reply");
 
-if ($role->canAssignTickets())
+if ($role->hasPerm(TicketModel::PERM_ASSIGN))
     $options[]=array('action'=>($ticket->isAssigned()?__('Reassign'):__('Assign')),'url'=>"tickets.php?id=$tid#assign");
 
-if ($role->canTransferTickets())
+if ($role->hasPerm(TicketModel::PERM_TRANSFER))
     $options[]=array('action'=>__('Transfer'),'url'=>"tickets.php?id=$tid#transfer");
 
 $options[]=array('action'=>__('Post Note'),'url'=>"tickets.php?id=$tid#note");
 
-if ($role->canEditTickets())
+if ($role->hasPerm(TicketModel::PERM_EDIT))
     $options[]=array('action'=>__('Edit Ticket'),'url'=>"tickets.php?id=$tid&a=edit");
 
 if($options) {
diff --git a/include/staff/ticket-edit.inc.php b/include/staff/ticket-edit.inc.php
index 5b464cdca0a2b2997e37125b4b810026ffaae02e..b1e8dcf77a5c00d0b06d7b7b046be334e3597f07 100644
--- a/include/staff/ticket-edit.inc.php
+++ b/include/staff/ticket-edit.inc.php
@@ -1,9 +1,7 @@
 <?php
 if (!defined('OSTSCPINC')
-        || !$thisstaff
         || !$ticket
-        || !($role=$thisstaff->getRole($ticket->getDeptId()))
-        || !$role->canEditTickets())
+        || !($ticket->checkStaffPerm($thisstaff, TicketModel::PERM_EDIT)))
     die('Access Denied');
 
 $info=Format::htmlchars(($errors && $_POST)?$_POST:$ticket->getUpdateInfo());
diff --git a/include/staff/ticket-open.inc.php b/include/staff/ticket-open.inc.php
index 642808356b95b644a5a0b9a99921c601cd57f714..fcebc13d474f66469e7eb2e188f55505de61e165 100644
--- a/include/staff/ticket-open.inc.php
+++ b/include/staff/ticket-open.inc.php
@@ -1,5 +1,8 @@
 <?php
-if(!defined('OSTSCPINC') || !$thisstaff || !$thisstaff->canCreateTickets()) die('Access Denied');
+if (!defined('OSTSCPINC') || !$thisstaff
+        || !$thisstaff->hasPerm(TicketModel::PERM_CREATE))
+        die('Access Denied');
+
 $info=array();
 $info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
 
@@ -223,7 +226,7 @@ if ($_POST)
         </tr>
 
         <?php
-        if($thisstaff->canAssignTickets()) { ?>
+        if($thisstaff->hasPerm(TicketModel::PERM_ASSIGN)) { ?>
         <tr>
             <td width="160"><?php echo __('Assign To');?>:</td>
             <td>
@@ -272,7 +275,7 @@ if ($_POST)
         <tbody>
         <?php
         //is the user allowed to post replies??
-        if($thisstaff->getRole()->canPostTicketReply()) { ?>
+        if ($thisstaff->getRole()->hasPerm(TicketModel::PERM_REPLY)) { ?>
         <tr>
             <th colspan="2">
                 <em><strong><?php echo __('Response');?></strong>: <?php echo __('Optional response to the above issue.');?></em>
@@ -326,7 +329,7 @@ print $response_form->getField('attachments')->render();
                     <?php
                     $statusId = $info['statusId'] ?: $cfg->getDefaultTicketStatusId();
                     $states = array('open');
-                    if ($thisstaff->canCloseTickets())
+                    if ($thisstaff->hasPerm(TicketModel::PERM_CLOSE))
                         $states = array_merge($states, array('closed'));
                     foreach (TicketStatusList::getStatuses(
                                 array('states' => $states)) as $s) {
diff --git a/include/staff/ticket-tasks.inc.php b/include/staff/ticket-tasks.inc.php
index d4cf59e5f133cd06dc5d243872752fa6e61c6429..8b8f3fb9d7c13f601ad6c101b4d2f7a32dd0bb43 100644
--- a/include/staff/ticket-tasks.inc.php
+++ b/include/staff/ticket-tasks.inc.php
@@ -1,4 +1,7 @@
 <?php
+global $thisstaff;
+
+$role = $thisstaff->getRole($ticket->getDeptId());
 
 $tasks = Task::objects()
     ->select_related('dept', 'staff')
@@ -23,7 +26,7 @@ $showing = $pageNav->showing().' '._N('task', 'tasks', $count);
 </div>
 <div style="float:right;text-align:right;padding-right:5px;">
     <?php
-    if ($ticket) { ?>
+    if ($role && $role->hasPerm(Task::PERM_CREATE)) { ?>
         <a
         class="Icon newTicket task-action"
         data-url="tickets.php?id=<?php echo $ticket->getId(); ?>#tasks"
diff --git a/include/staff/ticket-view.inc.php b/include/staff/ticket-view.inc.php
index e6522e7dee47fbb44e6a87aeb10e35379828b5e3..409eac800ab1399afc12f26c81faf90be9aec67d 100644
--- a/include/staff/ticket-view.inc.php
+++ b/include/staff/ticket-view.inc.php
@@ -3,7 +3,7 @@
 if(!defined('OSTSCPINC') || !$thisstaff || !is_object($ticket) || !$ticket->getId()) die('Invalid path');
 
 //Make sure the staff is allowed to access the page.
-if(!@$thisstaff->isStaff() || !$ticket->checkStaffAccess($thisstaff)) die('Access Denied');
+if(!@$thisstaff->isStaff() || !$ticket->checkStaffPerm($thisstaff)) die('Access Denied');
 
 //Re-use the post info on error...savekeyboards.org (Why keyboard? -> some people care about objects than users!!)
 $info=($_POST && $errors)?Format::input($_POST):array();
@@ -60,8 +60,8 @@ if($ticket->isOverdue())
         </td>
         <td width="auto" class="flush-right has_bottom_border">
             <?php
-            if ($role->canBanEmails()
-                    || $role->canEditTickets()
+            if ($role->hasPerm(EmailModel::PERM_BANLIST)
+                    || $role->hasPerm(TicketModel::PERM_EDIT)
                     || ($dept && $dept->isManager($thisstaff))) { ?>
             <span class="action-button pull-right" data-dropdown="#action-dropdown-more">
                 <i class="icon-caret-down pull-right"></i>
@@ -72,12 +72,13 @@ if($ticket->isOverdue())
             // Status change options
             echo TicketStatus::status_options();
 
-            if ($role->canEditTickets()) { ?>
+            if ($role->hasPerm(TicketModel::PERM_EDIT)) { ?>
                 <a class="action-button pull-right" href="tickets.php?id=<?php echo $ticket->getId(); ?>&a=edit"><i class="icon-edit"></i> <?php
                     echo __('Edit'); ?></a>
             <?php
             }
-            if ($ticket->isOpen() && !$ticket->isAssigned() && $role->canAssignTickets()) {?>
+            if ($ticket->isOpen() && !$ticket->isAssigned() &&
+                    $role->hasPerm(TicketModel::PERM_ASSIGN)) {?>
                 <a id="ticket-claim" class="action-button pull-right confirm-action" href="#claim"><i class="icon-user"></i> <?php
                     echo __('Claim'); ?></a>
 
@@ -99,13 +100,13 @@ if($ticket->isOverdue())
             <div id="action-dropdown-more" class="action-dropdown anchor-right">
               <ul>
                 <?php
-                 if ($role->canEditTickets()) { ?>
+                 if ($role->hasPerm(TicketModel::PERM_EDIT)) { ?>
                     <li><a class="change-user" href="#tickets/<?php
                     echo $ticket->getId(); ?>/change-user"><i class="icon-user"></i> <?php
                     echo __('Change Owner'); ?></a></li>
                 <?php
                  }
-                 if ($role->canDeleteTickets()) {
+                 if ($role->hasPerm(TicketModel::PERM_DELETE)) {
                      ?>
                     <li><a class="ticket-action" href="#tickets/<?php
                     echo $ticket->getId(); ?>/status/delete"
@@ -143,7 +144,7 @@ if($ticket->isOverdue())
                     return false"
                     ><i class="icon-paste"></i> <?php echo __('Manage Forms'); ?></a></li>
 
-<?php           if ($role->canBanEmails()) {
+<?php           if ($role->hasPerm(EmailModel::PERM_BANLIST)) {
                      if(!$emailBanned) {?>
                         <li><a class="confirm-action" id="ticket-banemail"
                             href="#banemail"><i class="icon-ban-circle"></i> <?php echo sprintf(
@@ -455,25 +456,25 @@ $tcount+= $ticket->getNumNotes();
 <div id="response_options">
     <ul class="tabs">
         <?php
-        if ($role->canPostTicketReply()) { ?>
+        if ($role->hasPerm(TicketModel::PERM_REPLY)) { ?>
         <li class="active"><a href="#reply"><?php echo __('Post Reply');?></a></li>
         <?php
         } ?>
         <li><a href="#note"><?php echo __('Post Internal Note');?></a></li>
         <?php
-        if ($role->canTransferTickets()) { ?>
+        if ($role->hasPerm(TicketModel::PERM_TRANSFER)) { ?>
         <li><a href="#transfer"><?php echo __('Department Transfer');?></a></li>
         <?php
         }
 
-        if ($role->canAssignTickets()) { ?>
+        if ($role->hasPerm(TicketModel::PERM_ASSIGN)) { ?>
         <li><a href="#assign"><?php
             echo $ticket->isAssigned()?__('Reassign Ticket'):__('Assign Ticket'); ?></a></li>
         <?php
         } ?>
     </ul>
     <?php
-    if ($role->canPostTicketReply()) { ?>
+    if ($role->hasPerm(TicketModel::PERM_REPLY)) { ?>
     <form id="reply" class="tab_content" action="tickets.php?id=<?php
         echo $ticket->getId(); ?>" name="reply" method="post" enctype="multipart/form-data">
         <?php csrf_token(); ?>
@@ -632,7 +633,7 @@ $tcount+= $ticket->getNumNotes();
                     <?php
                     $statusId = $info['reply_status_id'] ?: $ticket->getStatusId();
                     $states = array('open');
-                    if ($role->canCloseTickets() && !$outstanding)
+                    if ($role->hasPerm(TicketModel::PERM_CLOSE) && !$outstanding)
                         $states = array_merge($states, array('closed'));
 
                     foreach (TicketStatusList::getStatuses(
@@ -716,7 +717,7 @@ $tcount+= $ticket->getNumNotes();
                         <?php
                         $statusId = $info['note_status_id'] ?: $ticket->getStatusId();
                         $states = array('open');
-                        if ($role->canCloseTickets())
+                        if ($role->hasPerm(TicketModel::PERM_CLOSE))
                             $states = array_merge($states, array('closed'));
                         foreach (TicketStatusList::getStatuses(
                                     array('states' => $states)) as $s) {
@@ -742,7 +743,7 @@ $tcount+= $ticket->getNumNotes();
        </p>
    </form>
     <?php
-    if ($role->canTransferTickets()) { ?>
+    if ($role->hasPerm(TicketModel::PERM_TRANSFER)) { ?>
     <form id="transfer" class="hidden tab_content" action="tickets.php?id=<?php
         echo $ticket->getId(); ?>#transfer" name="transfer" method="post" enctype="multipart/form-data">
         <?php csrf_token(); ?>
@@ -803,7 +804,7 @@ $tcount+= $ticket->getNumNotes();
     <?php
     } ?>
     <?php
-    if ($role->canAssignTickets()) { ?>
+    if ($role->hasPerm(TicketModel::PERM_ASSIGN)) { ?>
     <form id="assign" class="hidden tab_content" action="tickets.php?id=<?php
          echo $ticket->getId(); ?>#assign" name="assign" method="post" enctype="multipart/form-data">
         <?php csrf_token(); ?>
diff --git a/include/staff/tickets.inc.php b/include/staff/tickets.inc.php
index 83f8b4e7e94cfff13c1d9196c944090226d56da8..ad4e0864595ba4f76f4c175675292fa8fb699190 100644
--- a/include/staff/tickets.inc.php
+++ b/include/staff/tickets.inc.php
@@ -199,7 +199,7 @@ $_SESSION[':Q:tickets'] = $tickets;
             if ($thisstaff->canManageTickets()) {
                 echo TicketStatus::status_options();
             }
-            if ($thisstaff->canDeleteTickets()) { ?>
+            if ($thisstaff->hasPerm(TicketModel::PERM_DELETE)) { ?>
             <a id="tickets-delete" class="action-button tickets-action"
                 href="#tickets/status/delete"><i
             class="icon-trash"></i> <?php echo __('Delete'); ?></a>
@@ -216,7 +216,7 @@ $_SESSION[':Q:tickets'] = $tickets;
  <table class="list" border="0" cellspacing="1" cellpadding="2" width="940">
     <thead>
         <tr>
-            <?php if($thisstaff->canManageTickets()) { ?>
+            <?php if ($thisstaff->canManageTickets()) { ?>
 	        <th width="8px">&nbsp;</th>
             <?php } ?>
 	        <th width="70">
diff --git a/include/upgrader/streams/core/1ee831c8-36f6b328.task.php b/include/upgrader/streams/core/1ee831c8-36f6b328.task.php
index 3b8ff65b41343cff6852899844942b3ed63b04ce..ef8cd369069ee874343ebf3083a3d4c2c688a17c 100644
--- a/include/upgrader/streams/core/1ee831c8-36f6b328.task.php
+++ b/include/upgrader/streams/core/1ee831c8-36f6b328.task.php
@@ -11,8 +11,8 @@ class GroupRoles extends MigrationTask {
             'can_assign_tickets'    => 'ticket.assign',
             'can_transfer_tickets'  => 'ticket.transfer',
             'can_ban_emails'        => 'emails.banlist',
-            'can_manage_premade'    => 'kb.premade',
-            'can_manage_faq'        => 'kb.faq',
+            'can_manage_premade'    => 'canned.manage',
+            'can_manage_faq'        => 'faq.manage',
             'can_view_staff_stats'  => 'stats.agents',
     );
 
diff --git a/scp/attachment.php b/scp/attachment.php
index ab6d45ab14d31d4122c085f40188c125bb74d897..d3283b877bd1300ad12d93d77bd7362a12943a41 100644
--- a/scp/attachment.php
+++ b/scp/attachment.php
@@ -30,7 +30,7 @@ if (!$thisstaff
         )
     Http::response(404, __('Unknown or invalid file'));
 
-if (!$ticket->checkStaffAccess($thisstaff))
+if (!$ticket->checkStaffPerm($thisstaff))
     die(__('Access Denied'));
 
 //Download the file..
diff --git a/scp/canned.php b/scp/canned.php
index d6a9ffa9af4858d4a0c8585d09daf4575ea85007..c8290c39f58b9f87494bb11f6ad436f329d598b6 100644
--- a/scp/canned.php
+++ b/scp/canned.php
@@ -17,7 +17,9 @@ require('staff.inc.php');
 include_once(INCLUDE_DIR.'class.canned.php');
 
 /* check permission */
-if(!$thisstaff || !$thisstaff->getRole()->canManageCannedResponses()) {
+if(!$thisstaff
+        ||
+        !$thisstaff->getRole()->hasPerm(CannedModel::PERM_MANAGE)) {
     header('Location: kb.php');
     exit;
 }
@@ -35,7 +37,7 @@ $canned_form = new Form(array(
    )),
 ));
 
-if($_POST && $thisstaff->getRole()->canManageCannedResponses()) {
+if ($_POST) {
     switch(strtolower($_POST['do'])) {
         case 'update':
             if(!$canned) {
diff --git a/scp/categories.php b/scp/categories.php
index 059e52d8064cfd12b6df0167e17ae299bcd2eede..d4e6125b4bca4d3fee6d40544d1ec173c408ff17 100644
--- a/scp/categories.php
+++ b/scp/categories.php
@@ -17,7 +17,8 @@ require('staff.inc.php');
 include_once(INCLUDE_DIR.'class.category.php');
 
 /* check permission */
-if(!$thisstaff || !$thisstaff->getRole()->canManageFAQ()) {
+if(!$thisstaff ||
+        !$thisstaff->getRole()->hasPerm(FAQ::PERM_MANAGE)) {
     header('Location: kb.php');
     exit;
 }
diff --git a/scp/emailsettings.php b/scp/emailsettings.php
index 175fa4575b2dfc2ac79246782b91adf11e97d2d5..6f871b4bf7b62e072689f4672b9df64f15bb5e06 100644
--- a/scp/emailsettings.php
+++ b/scp/emailsettings.php
@@ -35,5 +35,5 @@ $nav->setTabActive('emails', 'emailsettings.php');
 require_once(STAFFINC_DIR.'header.inc.php');
 include_once(STAFFINC_DIR.$inc);
 include_once(STAFFINC_DIR.'footer.inc.php');
-?>
 
+?>
diff --git a/scp/faq.php b/scp/faq.php
index 322420fd796e389b8a7f1b19b5203ae1332db4e0..03c5f160118ba06081f1e53b33afc867a84d9b10 100644
--- a/scp/faq.php
+++ b/scp/faq.php
@@ -150,14 +150,17 @@ else {
     }
 }
 
+$role = $thisstaff->getRole();
 $inc='faq-categories.inc.php'; //FAQs landing page.
 if($faq) {
     $inc='faq-view.inc.php';
-    if($_REQUEST['a']=='edit' && $thisstaff->getRole()->canManageFAQ())
+    if ($_REQUEST['a']=='edit'
+            && $role->hasPerm(FAQ::PERM_MANAGE))
         $inc='faq.inc.php';
     elseif ($_REQUEST['a'] == 'print')
         return $faq->printPdf();
-}elseif($_REQUEST['a']=='add' && $thisstaff->getRole()->canManageFAQ()) {
+}elseif($_REQUEST['a']=='add'
+        && $role->hasPerm(FAQ::PERM_MANAGE)) {
     $inc='faq.inc.php';
 } elseif($category && $_REQUEST['a']!='search') {
     $inc='faq-category.inc.php';
diff --git a/scp/roles.php b/scp/roles.php
index 02bb0fec0c8d663c2e5e014e03a24c72cbffcbb0..2c9476e29b8cb7499b01f7d5e7d171fe980af980 100644
--- a/scp/roles.php
+++ b/scp/roles.php
@@ -14,7 +14,11 @@
     vim: expandtab sw=4 ts=4 sts=4:
 **********************************************************************/
 
-require('admin.inc.php');
+require 'admin.inc.php';
+include_once INCLUDE_DIR . 'class.canned.php';
+include_once INCLUDE_DIR . 'class.faq.php';
+include_once INCLUDE_DIR . 'class.email.php';
+include_once INCLUDE_DIR . 'class.report.php';
 
 $errors = array();
 $role=null;
diff --git a/scp/tickets.php b/scp/tickets.php
index d79a299bd4e88cf7d5582e82ea8016e59ca1fcb0..78da72d76421cfaad6828e0bdebf6b5d7b4a504f 100644
--- a/scp/tickets.php
+++ b/scp/tickets.php
@@ -29,7 +29,7 @@ $ticket = $user = null; //clean start.
 if($_REQUEST['id']) {
     if(!($ticket=Ticket::lookup($_REQUEST['id'])))
          $errors['err']=sprintf(__('%s: Unknown or invalid ID.'), __('ticket'));
-    elseif(!$ticket->checkStaffAccess($thisstaff)) {
+    elseif(!$ticket->checkStaffPerm($thisstaff)) {
         $errors['err']=__('Access denied. Contact admin if you believe this is in error');
         $ticket=null; //Clear ticket obj.
     }
@@ -61,7 +61,7 @@ if($_POST && !$errors):
         $role = $thisstaff->getRole($ticket->getDeptId());
         switch(strtolower($_POST['a'])):
         case 'reply':
-            if(!$role || !$role->canPostTicketReply())
+            if(!$role || !$role->hasPerm(TicketModel::PERM_REPLY))
                 $errors['err'] = __('Action denied. Contact admin for access');
             else {
 
@@ -109,7 +109,7 @@ if($_POST && !$errors):
             break;
         case 'transfer': /** Transfer ticket **/
             //Check permission
-            if(!$role->canTransferTickets())
+            if(!$role->hasPerm(TicketModel::PERM_TRANSFER))
                 $errors['err']=$errors['transfer'] = __('Action Denied. You are not allowed to transfer tickets.');
             else {
 
@@ -132,7 +132,7 @@ if($_POST && !$errors):
                     $msg = sprintf(__('Ticket transferred successfully to %s'),
                             $ticket->getDept()->getFullName());
                     //Check to make sure the staff still has access to the ticket
-                    if(!$ticket->checkStaffAccess($thisstaff))
+                    if(!$ticket->checkStaffPerm($thisstaff))
                         $ticket=null;
 
                 } elseif(!$errors['transfer']) {
@@ -143,7 +143,7 @@ if($_POST && !$errors):
             break;
         case 'assign':
 
-             if(!$role->canAssignTickets())
+             if(!$role->hasPerm(TicketModel::PERM_ASSIGN))
                  $errors['err']=$errors['assign'] = __('Action Denied. You are not allowed to assign/reassign tickets.');
              else {
 
@@ -214,13 +214,13 @@ if($_POST && !$errors):
             break;
         case 'edit':
         case 'update':
-            if(!$ticket || !$role->canEditTickets())
+            if(!$ticket || !$role->hasPerm(TicketModel::PERM_EDIT))
                 $errors['err']=__('Permission Denied. You are not allowed to edit tickets');
             elseif($ticket->update($_POST,$errors)) {
                 $msg=__('Ticket updated successfully');
                 $_REQUEST['a'] = null; //Clear edit action - going back to view.
                 //Check to make sure the staff STILL has access post-update (e.g dept change).
-                if(!$ticket->checkStaffAccess($thisstaff))
+                if(!$ticket->checkStaffPerm($thisstaff))
                     $ticket=null;
             } elseif(!$errors['err']) {
                 $errors['err']=__('Unable to update the ticket. Correct the errors below and try again!');
@@ -242,7 +242,7 @@ if($_POST && !$errors):
                     }
                     break;
                 case 'claim':
-                    if(!$role->canAssignTickets()) {
+                    if(!$role->hasPerm(TicketModel::PERM_EDIT)) {
                         $errors['err'] = __('Permission Denied. You are not allowed to assign/claim tickets.');
                     } elseif(!$ticket->isOpen()) {
                         $errors['err'] = __('Only open tickets can be assigned');
@@ -288,7 +288,7 @@ if($_POST && !$errors):
                     }
                     break;
                 case 'banemail':
-                    if(!$role->canBanEmails()) {
+                    if (!$role->hasPerm(EmailModel::PERM_BANLIST)) {
                         $errors['err']=__('Permission Denied. You are not allowed to ban emails');
                     } elseif(BanList::includes($ticket->getEmail())) {
                         $errors['err']=__('Email already in banlist');
@@ -299,7 +299,7 @@ if($_POST && !$errors):
                     }
                     break;
                 case 'unbanemail':
-                    if(!$role->canBanEmails()) {
+                    if (!$role->hasPerm(EmailModel::PERM_BANLIST)) {
                         $errors['err'] = __('Permission Denied. You are not allowed to remove emails from banlist.');
                     } elseif(Banlist::remove($ticket->getEmail())) {
                         $msg = __('Email removed from banlist');
@@ -310,7 +310,7 @@ if($_POST && !$errors):
                     }
                     break;
                 case 'changeuser':
-                    if (!$role->canEditTickets()) {
+                    if (!$role->hasPerm(TicketModel::PERM_EDIT)) {
                         $errors['err']=__('Permission Denied. You are not allowed to edit tickets');
                     } elseif (!$_POST['user_id'] || !($user=User::lookup($_POST['user_id']))) {
                         $errors['err'] = __('Unknown user selected');
@@ -335,7 +335,8 @@ if($_POST && !$errors):
         switch($_POST['a']) {
             case 'open':
                 $ticket=null;
-                if(!$thisstaff || !$thisstaff->canCreateTickets()) {
+                if (!$thisstaff ||
+                        !$thisstaff->hasPerm(TicketModel::PERM_CREATE)) {
                      $errors['err'] = sprintf('%s %s',
                              sprintf(__('You do not have permission %s.'),
                                  __('to create tickets')),
@@ -349,7 +350,7 @@ if($_POST && !$errors):
                     if(($ticket=Ticket::open($vars, $errors))) {
                         $msg=__('Ticket created successfully');
                         $_REQUEST['a']=null;
-                        if (!$ticket->checkStaffAccess($thisstaff) || $ticket->isClosed())
+                        if (!$ticket->checkStaffPerm($thisstaff) || $ticket->isClosed())
                             $ticket=null;
                         Draft::deleteForNamespace('ticket.staff%', $thisstaff->getId());
                         // Drop files from the response attachments widget
@@ -455,7 +456,7 @@ if (isset($_SESSION['advsearch'])) {
                         (!$_REQUEST['status'] || $_REQUEST['status']=='search'));
 }
 
-if($thisstaff->canCreateTickets()) {
+if ($thisstaff->hasPerm(TicketModel::PERM_CREATE)) {
     $nav->addSubMenu(array('desc'=>__('New Ticket'),
                            'title'=> __('Open a New Ticket'),
                            'href'=>'tickets.php?a=open',
@@ -474,7 +475,7 @@ if($ticket) {
     $nav->setActiveSubMenu(-1);
     $inc = 'ticket-view.inc.php';
     if ($_REQUEST['a']=='edit'
-            && $thisstaff->getRole($ticket->getDeptId())->canEditTickets()) {
+            && $ticket->checkStaffPerm($thisstaff, TicketModel::PERM_EDIT)) {
         $inc = 'ticket-edit.inc.php';
         if (!$forms) $forms=DynamicFormEntry::forTicket($ticket->getId());
         // Auto add new fields to the entries
@@ -483,7 +484,8 @@ if($ticket) {
         $errors['err'] = __('Internal error: Unable to export the ticket to PDF for print.');
 } else {
 	$inc = 'tickets.inc.php';
-    if($_REQUEST['a']=='open' && $thisstaff->canCreateTickets())
+    if ($_REQUEST['a']=='open' &&
+            $thisstaff->hasPerm(TicketModel::PERM_CREATE))
         $inc = 'ticket-open.inc.php';
     elseif($_REQUEST['a'] == 'export') {
         $ts = strftime('%Y%m%d');