diff --git a/include/class.export.php b/include/class.export.php
index 6b7b4be5d54319e6f886046540f0b1daf31f4070..b08291c24980be7790135e67bdadf990872b654f 100644
--- a/include/class.export.php
+++ b/include/class.export.php
@@ -137,7 +137,7 @@ class Export {
                 'attachment_count' => __('Attachment Count'),
             ) + $cdata,
             $how,
-            array('modify' => function(&$record, $keys) use ($fields) {
+            array('modify' => function(&$record, $keys, $obj) use ($fields) {
                 foreach ($fields as $k=>$f) {
                     if (($i = array_search($k, $keys)) !== false) {
                         $record[$i] = $f->export($f->to_php($record[$i]));
@@ -182,7 +182,7 @@ class Export {
                     '::getEmail' =>          __('Email'),
                     ) + $cdata,
                 $how,
-                array('modify' => function(&$record, $keys) use ($fields) {
+                array('modify' => function(&$record, $keys, $obj) use ($fields) {
                     foreach ($fields as $k=>$f) {
                         if ($f && ($i = array_search($k, $keys)) !== false) {
                             $record[$i] = $f->export($f->to_php($record[$i]));
@@ -221,7 +221,7 @@ class Export {
                     'name'  =>  'Name',
                     ) + $cdata,
                 $how,
-                array('modify' => function(&$record, $keys) use ($fields) {
+                array('modify' => function(&$record, $keys, $obj) use ($fields) {
                     foreach ($fields as $k=>$f) {
                         if ($f && ($i = array_search($k, $keys)) !== false) {
                             $record[$i] = $f->export($f->to_php($record[$i]));
@@ -239,6 +239,38 @@ class Export {
         return false;
     }
 
+    static function  agents($agents, $filename='', $how='csv') {
+
+        // Filename or stream to export agents to
+        $filename = $filename ?: sprintf('Agents-%s.csv',
+                strftime('%Y%m%d'));
+        Http::download($filename, "text/$how");
+        $depts = Dept::getDepartments();
+        echo self::dumpQuery($agents, array(
+                    '::getName'  =>  'Name',
+                    '::getUsername' => 'Username',
+                    '::getStatus' => 'Status',
+                    'permissions' => 'Permissions',
+                    '::getDept'  => 'Primary Department',
+                    ) + $depts,
+                $how,
+                array('modify' => function(&$record, $keys, $obj) use ($depts) {
+
+                   if (($i = array_search('permissions', $keys)))
+                       $record[$i] = implode(",", array_keys($obj->getPermission()->getInfo()));
+
+                    $roles = $obj->getRoles();
+                    foreach ($depts as $k => $v) {
+                        if (is_numeric($k) && ($i = array_search($k, $keys)) !== false) {
+                            $record[$i] = $roles[$k] ?: '';
+                        }
+                    }
+                    return $record;
+                    })
+                );
+        exit;
+
+    }
 }
 
 class ResultSetExporter {
@@ -276,26 +308,32 @@ class ResultSetExporter {
         $this->_res->next();
 
         $record = array();
-
         foreach ($this->keys as $field) {
             list($field, $func) = explode('::', $field);
             $path = explode('.', $field);
+
             $current = $object;
             // Evaluate dotted ORM path
             if ($field) {
                 foreach ($path as $P) {
-                    $current = $current->{$P};
+                    if (isset($current->{$P}))
+                        $current = $current->{$P};
+                    else  {
+                        $current = $P;
+                        break;
+                    }
                 }
             }
             // Evalutate :: function call on target current
             if ($func && (method_exists($current, $func) || method_exists($current, '__call'))) {
                 $current = $current->{$func}();
             }
+
             $record[] = (string) $current;
         }
 
         if (isset($this->options['modify']) && is_callable($this->options['modify']))
-            $record = $this->options['modify']($record, $this->keys);
+            $record = $this->options['modify']($record, $this->keys, $object);
 
         return $record;
     }
@@ -321,17 +359,7 @@ class CsvResultsExporter extends ResultSetExporter {
         if (!$this->output)
              $this->output = fopen('php://output', 'w');
 
-        // Detect delimeter from the current locale settings. For locales
-        // which use comma (,) as the decimal separator, the semicolon (;)
-        // should be used as the field separator
-        $delimiter = ',';
-        if (class_exists('NumberFormatter')) {
-            $nf = NumberFormatter::create(Internationalization::getCurrentLocale(),
-                NumberFormatter::DECIMAL);
-            $s = $nf->getSymbol(NumberFormatter::DECIMAL_SEPARATOR_SYMBOL);
-            if ($s == ',')
-                $delimiter = ';';
-        }
+        $delimiter = Internationalization::getCSVDelimiter();
 
         // Output a UTF-8 BOM (byte order mark)
         fputs($this->output, chr(0xEF) . chr(0xBB) . chr(0xBF));
diff --git a/include/class.i18n.php b/include/class.i18n.php
index 0a08736d76d84cc4e120cd73215a48a161a659f0..0dc60bde0707e682bd2ce4d774fdafbfce88bc4a 100644
--- a/include/class.i18n.php
+++ b/include/class.i18n.php
@@ -408,6 +408,30 @@ class Internationalization {
         return $locale;
     }
 
+    static function getCSVDelimiter($locale='') {
+
+        if (!$locale)  // Prefer browser settings
+            $locale = Locale::acceptFromHttp($_SERVER['HTTP_ACCEPT_LANGUAGE']);
+
+        // Detect delimeter from the current locale settings. For locales
+        // which use comma (,) as the decimal separator, the semicolon (;)
+        // should be used as the field separator
+        $delimiter = ',';
+        if (class_exists('NumberFormatter')) {
+            $nf = NumberFormatter::create($locale ?: self::getCurrentLocale(),
+                NumberFormatter::DECIMAL);
+            $s = $nf->getSymbol(NumberFormatter::DECIMAL_SEPARATOR_SYMBOL);
+            if ($s == ',')
+                $delimiter = ';';
+        } else {
+            $info = localeconv();
+            if ($info && $info['decimal_point'] == ',')
+                $delimiter = ';';
+
+        }
+
+        return $delimiter;
+    }
 
     //  getIntDateFormatter($options)
     //
diff --git a/include/class.staff.php b/include/class.staff.php
index 5a20d35b5b24e7618143a540f9b4883cc986de71..9d4df38cc19ccdba8dc0095236881e4d4f7e2482 100644
--- a/include/class.staff.php
+++ b/include/class.staff.php
@@ -449,23 +449,27 @@ implements AuthenticatedUser, EmailContact, TemplateVariable, Searchable {
         return isset($this->locale) ? $this->locale : 0;
     }
 
-    function getRole($dept=null, $useDefault=true) {
-        $deptId = is_object($dept) ? $dept->getId() : $dept;
-        if ($deptId && $deptId != $this->dept_id) {
-            if (isset($this->_roles[$deptId]))
-                return $this->_roles[$deptId];
+    function getRoles() {
+        if (!isset($this->_roles)) {
+            $this->_roles = array($this->dept_id => $this->role);
+            foreach($this->dept_access as $da)
+                $this->_roles[$da->dept_id] = $da->role;
+        }
+
+        return $this->_roles;
+    }
 
-            if ($access = $this->dept_access->findFirst(array('dept_id' => $deptId)))
-                return $this->_roles[$deptId] = $access->role;
+    function getRole($dept=null) {
+        $deptId = is_object($dept) ? $dept->getId() : $dept;
+        $roles = $this->getRoles();
+        if (isset($roles[$deptId]))
+            return $roles[$deptId];
 
-            if (!$useDefault || !$this->usePrimaryRoleOnAssignment())
-                // View only access
-                return new Role(array());
+        if ($this->usePrimaryRoleOnAssignment())
+            return $this->role;
 
-            // Fall through to primary role
-        }
-        // For the primary department, use the primary role
-        return $this->role;
+        // View only access
+        return new Role(array());
     }
 
     function hasPerm($perm, $global=true) {
@@ -494,10 +498,14 @@ implements AuthenticatedUser, EmailContact, TemplateVariable, Searchable {
         return TRUE;
     }
 
-    function isactive() {
+    function isActive() {
         return $this->isactive;
     }
 
+    function getStatus() {
+        return $this->isActive() ? __('Active') : __('Locked');
+    }
+
     function isVisible() {
          return $this->isvisible;
     }
@@ -507,7 +515,7 @@ implements AuthenticatedUser, EmailContact, TemplateVariable, Searchable {
     }
 
     function isAvailable() {
-        return ($this->isactive() && !$this->onVacation());
+        return ($this->isActive() && !$this->onVacation());
     }
 
     function showAssignedOnly() {
@@ -1174,6 +1182,15 @@ implements AuthenticatedUser, EmailContact, TemplateVariable, Searchable {
         return true;
     }
 
+    static function export($criteria=null, $filename='') {
+        include_once(INCLUDE_DIR.'class.error.php');
+
+        $agents = Staff::objects();
+        // Sort based on name formating
+        $agents = self::nsort($agents);
+        Export::agents($agents, $filename);
+    }
+
 }
 
 interface RestrictedAccess {
diff --git a/include/staff/staffmembers.inc.php b/include/staff/staffmembers.inc.php
index 97f7c1265249a845d5bfb49d3705e66638e44569..9c3c76020c4e04ad6d17f014ac9eea413a057073 100644
--- a/include/staff/staffmembers.inc.php
+++ b/include/staff/staffmembers.inc.php
@@ -143,6 +143,12 @@ $agents->limit($pageNav->getLimit())->offset($pageNav->getStart());
                                 <?php echo __( 'Change Department'); ?>
                             </a>
                         </li>
+                        <li>
+                            <a class="no-pjax" href="staff.php?a=export">
+                                <i class="icon-download-alt icon-fixed-width"></i>
+                                <?php echo __( 'Export Agents'); ?>
+                            </a>
+                        </li>
                         <!-- TODO: Implement "Reset Access" mass action
                     <li><a class="dialog-first" href="#staff/reset-access">
                     <i class="icon-puzzle-piece icon-fixed-width"></i>
diff --git a/scp/staff.php b/scp/staff.php
index 9b2f4a999c6b6d65b10077dc3ededd2769129a89..c2731b9f2d69b7b0ff96d11f35d4c00782c5dd40 100644
--- a/scp/staff.php
+++ b/scp/staff.php
@@ -171,8 +171,11 @@ if($_POST){
 
 $page='staffmembers.inc.php';
 $tip_namespace = 'staff.agent';
-if($staff || ($_REQUEST['a'] && !strcasecmp($_REQUEST['a'],'add'))) {
+if ($staff || ($_REQUEST['a'] && !strcasecmp($_REQUEST['a'],'add'))) {
     $page='staff.inc.php';
+} elseif ($_REQUEST['a'] && !strcasecmp($_REQUEST['a'],'export')) {
+    if (!Staff::export())
+        $errors['err'] = sprintf(__('Unable to export %s.'), __('Agents'));
 }
 
 $nav->setTabActive('staff');