diff --git a/bootstrap.php b/bootstrap.php
index 47f617b62aeb63317b897aa2e95ebf91755460f2..a49d8dcd98530af5842123898776108b9411c606 100644
--- a/bootstrap.php
+++ b/bootstrap.php
@@ -94,14 +94,17 @@ class Bootstrap {
         define('THREAD_ENTRY_TABLE', $prefix.'thread_entry');
         define('THREAD_ENTRY_EMAIL_TABLE', $prefix.'thread_entry_email');
 
-        define('TICKET_TABLE',$prefix.'ticket');
         define('LOCK_TABLE',$prefix.'lock');
+
+        define('TICKET_TABLE',$prefix.'ticket');
+        define('TICKET_CDATA_TABLE', $prefix.'ticket__cdata');
         define('TICKET_EVENT_TABLE',$prefix.'ticket_event');
         define('THREAD_COLLABORATOR_TABLE', $prefix.'thread_collaborator');
         define('TICKET_STATUS_TABLE', $prefix.'ticket_status');
         define('TICKET_PRIORITY_TABLE',$prefix.'ticket_priority');
 
-        define('TASK_TABLE',$prefix.'task');
+        define('TASK_TABLE', $prefix.'task');
+        define('TASK_CDATA_TABLE', $prefix.'task__cdata');
 
         define('PRIORITY_TABLE',TICKET_PRIORITY_TABLE);
 
diff --git a/include/ajax.content.php b/include/ajax.content.php
index 0706757641e7bd82c5603218bafb76110aa92971..c12d56ab00bdd63863b2dee5e96ccac82a0bc592 100644
--- a/include/ajax.content.php
+++ b/include/ajax.content.php
@@ -150,7 +150,7 @@ class ContentAjaxAPI extends AjaxController {
         $langs = Internationalization::getConfiguredSystemLanguages();
         $translations = $content->getAllTranslations();
         $info = array(
-            'title' => $content->getTitle(),
+            'title' => $content->getName(),
             'body' => $content->getBody(),
         );
         foreach ($translations as $t) {
diff --git a/include/ajax.search.php b/include/ajax.search.php
index d52d7ca4784db657856df3e351f1fc1967679522..d228018e911f963d9ec8b201aca8d2921e201c70 100644
--- a/include/ajax.search.php
+++ b/include/ajax.search.php
@@ -74,7 +74,7 @@ class SearchAjaxAPI extends AjaxController {
             $field->form->getLocal('title'), $field->getLocal('label')
         ));
         $fields = SavedSearch::getSearchField($impl, $name);
-        $form = new Form($fields);
+        $form = new SimpleForm($fields);
         // Check the box to search the field by default
         if ($F = $form->getField("{$name}+search"))
             $F->value = true;
diff --git a/include/ajax.tasks.php b/include/ajax.tasks.php
index cb6728a2dbd0eee8245c2050184e4cb1c795cffe..8181b4eaea1646aba8e229a96a6ac8fd9d2755b2 100644
--- a/include/ajax.tasks.php
+++ b/include/ajax.tasks.php
@@ -22,6 +22,46 @@ require_once(INCLUDE_DIR.'class.task.php');
 
 class TasksAjaxAPI extends AjaxController {
 
+    function add() {
+        global $thisstaff;
+
+        $info=$errors=array();
+        if ($_POST) {
+            Draft::deleteForNamespace('task.add', $thisstaff->getId());
+            // Default form
+            $form = TaskForm::getInstance();
+            $form->setSource($_POST);
+            // Internal form
+            $iform = TaskForm::getInternalForm($_POST);
+            $isvalid = true;
+            if (!$iform->isValid())
+                $isvalid = false;
+            if (!$form->isValid())
+                $isvalid = false;
+
+            if ($isvalid) {
+                $vars = $_POST;
+                $vars['default_formdata'] = $form->getClean();
+                $vars['internal_formdata'] = $iform->getClean();
+                $desc = $form->getField('description');
+                if ($desc
+                        && $desc->isAttachmentsEnabled()
+                        && ($attachments=$desc->getWidget()->getAttachments()))
+                    $vars['cannedattachments'] = $attachments->getClean();
+                $vars['staffId'] = $thisstaff->getId();
+                $vars['poster'] = $thisstaff;
+                $vars['ip_address'] = $_SERVER['REMOTE_ADDR'];
+                if (($task=Task::create($vars, $errors)))
+                    Http::response(201, $task->getId());
+            }
+
+            $info['error'] = __('Error adding task - try again!');
+        }
+
+        include STAFFINC_DIR . 'templates/task.tmpl.php';
+    }
+
+
     function preview($tid) {
         global $thisstaff;
 
@@ -54,6 +94,220 @@ class TasksAjaxAPI extends AjaxController {
         include STAFFINC_DIR . 'templates/task-edit.tmpl.php';
     }
 
+    function massProcess($action)  {
+        global $thisstaff;
+
+        $actions = array(
+                'transfer' => array(
+                    'verbed' => __('transferred'),
+                    ),
+                'assign' => array(
+                    'verbed' => __('assigned'),
+                    ),
+                'delete' => array(
+                    'verbed' => __('deleted'),
+                    ),
+                'reopen' => array(
+                    'verbed' => __('reopen'),
+                    ),
+                'close' => array(
+                    'verbed' => __('closed'),
+                    ),
+                );
+
+        if (!isset($actions[$action]))
+            Http::response(404, __('Unknown action'));
+
+
+        $errors = $e = array();
+        $inc = null;
+        $i = $count = 0;
+        if ($_POST) {
+            if (!$_POST['tids'] || !($count=count($_POST['tids'])))
+                $errors['err'] = sprintf(
+                        __('You must select at least %s.'),
+                        __('one task'));
+        } else {
+            $count  =  $_REQUEST['count'];
+        }
+
+        switch ($action) {
+        case 'assign':
+            $inc = 'task-assign.tmpl.php';
+            $form = AssignmentForm::instantiate($_POST);
+            if ($_POST && $form->isValid()) {
+                foreach ($_POST['tids'] as $tid) {
+                    if (($t=Task::lookup($tid))
+                            // Make sure the agent is allowed to
+                            // access and assign the task.
+                            && $t->checkStaffPerm($thisstaff, Task::PERM_ASSIGN)
+                            // Do the transfer
+                            && $t->assign($form, $e)
+                            )
+                        $i++;
+                }
+
+                if (!$i) {
+                    $info['error'] = sprintf(
+                            __('Unable to %1$s %2$s'),
+                            __('assign'),
+                            _N('selected task', 'selected tasks', $count));
+                }
+            }
+            break;
+        case 'transfer':
+            $inc = 'task-transfer.tmpl.php';
+            $form = TransferForm::instantiate($_POST);
+            if ($_POST && $form->isValid()) {
+                foreach ($_POST['tids'] as $tid) {
+                    if (($t=Task::lookup($tid))
+                            // Make sure the agent is allowed to
+                            // access and transfer the task.
+                            && $t->checkStaffPerm($thisstaff, Task::PERM_TRANSFER)
+                            // Do the transfer
+                            && $t->transfer($form, $e)
+                            )
+                        $i++;
+                }
+
+                if (!$i) {
+                    $info['error'] = sprintf(
+                            __('Unable to %1$s %2$s'),
+                            __('transfer'),
+                            _N('selected task', 'selected tasks', $count));
+                }
+            }
+            break;
+        case 'reopen':
+            $info['status'] = 'open';
+        case 'close':
+            $inc = 'task-status.tmpl.php';
+            $info['status'] = $info['status'] ?: 'closed';
+            $perm = '';
+            switch ($info['status']) {
+            case 'open':
+                // If an agent can create a task then they're allowed to
+                // reopen closed ones.
+                $perm = Task::PERM_CREATE;
+                break;
+            case 'closed':
+                $perm = Task::PERM_CLOSE;
+                break;
+            default:
+                $errors['err'] = __('Unknown action');
+            }
+            // Check generic permissions --  department specific permissions
+            // will be checked below.
+            if ($perm && !$thisstaff->hasPerm($perm))
+                $errors['err'] = sprintf(
+                        __('You do not have permission to %s %s'),
+                        __($action),
+                        __('tasks'));
+
+            if ($_POST && !$errors) {
+                if (!$_POST['status']
+                        || !in_array($_POST['status'], array('open', 'closed')))
+                    $errors['status'] = __('Status selection required');
+                else {
+                    foreach ($_POST['tids'] as $tid) {
+                        if (($t=Task::lookup($tid))
+                                && $t->checkStaffPerm($thisstaff, $perm ?: null)
+                                && $t->setStatus($_POST['status'], $_POST['comments'])
+                                )
+                            $i++;
+                    }
+
+                    if (!$i) {
+                        $info['error'] = sprintf(
+                                __('Unable to change status of %1$s'),
+                                _N('selected task', 'selected tasks', $count));
+                    }
+                }
+            }
+            break;
+        case 'delete':
+            $inc = 'task-delete.tmpl.php';
+            $info[':placeholder'] = sprintf(__(
+                        'Optional reason for deleting %s'),
+                    _N('selected task', 'selected tasks', $count));
+            $info['warn'] = sprintf(__(
+                        'Are you sure you want to DELETE %s?'),
+                    _N('selected task', 'selected tasks', $count));
+            $info[':extra'] = sprintf('<strong>%s</strong>',
+                        __('Deleted tasks CANNOT be recovered, including any associated attachments.')
+                        );
+
+            if ($_POST && !$errors) {
+                foreach ($_POST['tids'] as $tid) {
+                    if (($t=Task::lookup($tid))
+                            && $t->getDeptId() != $_POST['dept_id']
+                            && $t->checkStaffPerm($thisstaff, Task::PERM_DELETE)
+                            && $t->delete($_POST, $e)
+                            )
+                        $i++;
+                }
+
+                if (!$i) {
+                    $info['error'] = sprintf(
+                            __('Unable to %1$s %2$s'),
+                            __('delete'),
+                            _N('selected task', 'selected tasks', $count));
+                }
+            }
+            break;
+        default:
+            Http::response(404, __('Unknown action'));
+        }
+
+
+        if ($_POST && $i) {
+
+            // Assume success
+            if ($i==$count) {
+                $msg = sprintf(__('Successfully %s %s.'),
+                        $actions[$action]['verbed'],
+                        sprintf(__('%1$d %2$s'),
+                            $count,
+                            _N('selected task', 'selected tasks', $count))
+                        );
+                $_SESSION['::sysmsgs']['msg'] = $msg;
+            } else {
+                $warn = sprintf(
+                        __('%1$d of %2$d %3$s %4$s'), $i, $count,
+                        _N('selected task', 'selected tasks',
+                            $count),
+                        $actions[$action]['verbed']);
+                $_SESSION['::sysmsgs']['warn'] = $warn;
+            }
+            Http::response(201, 'processed');
+        } elseif($_POST && !isset($info['error'])) {
+            $info['error'] = $errors['err'] ?: sprintf(
+                    __('Unable to %1$s  %2$s'),
+                    __('process'),
+                    _N('selected task', 'selected tasks', $count));
+        }
+
+        if ($_POST)
+            $info = array_merge($info, Format::htmlchars($_POST));
+
+
+        include STAFFINC_DIR . "templates/$inc";
+        //  Copy checked tasks to the form.
+        echo "
+        <script type=\"text/javascript\">
+        $(function() {
+            $('form#tasks input[name=\"tids[]\"]:checkbox:checked')
+            .each(function() {
+                $('<input>')
+                .prop('type', 'hidden')
+                .attr('name', 'tids[]')
+                .val($(this).val())
+                .appendTo('form.mass-action');
+            });
+        });
+        </script>";
+    }
+
     function transfer($tid) {
         global $thisstaff;
 
@@ -63,37 +317,76 @@ class TasksAjaxAPI extends AjaxController {
         if (!$task->checkStaffPerm($thisstaff, Task::PERM_TRANSFER))
             Http::response(403, __('Permission Denied'));
 
-        $info = $errors = array();
-        if ($_POST) {
-            if ($task->transfer($_POST,  $errors)) {
+        $errors = array();
+
+        $info = array(
+                ':title' => sprintf(__('Task #%s: %s'),
+                    $task->getNumber(),
+                    __('Tranfer')),
+                ':action' => sprintf('#tasks/%d/transfer',
+                    $task->getId())
+                );
+
+        $form = $task->getTransferForm($_POST);
+        if ($_POST && $form->isValid()) {
+            if ($task->transfer($form, $errors)) {
+                $_SESSION['::sysmsgs']['msg'] = sprintf(
+                        __('%s successfully'),
+                        sprintf(
+                            __('%s transferred to %s department'),
+                            __('Task'),
+                            $task->getDept()
+                            )
+                        );
                 Http::response(201, $task->getId());
-
             }
 
-            $info = Format::htmlchars($_POST);
+            $form->addErrors($errors);
             $info['error'] = $errors['err'] ?: __('Unable to transfer task');
         }
 
+        $info['dept_id'] = $info['dept_id'] ?: $task->getDeptId();
+
         include STAFFINC_DIR . 'templates/task-transfer.tmpl.php';
     }
 
     function assign($tid) {
         global $thisstaff;
 
-        if(!($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)) {
-                Http::response(201, $task->getId());
+        $errors = array();
+        $info = array(
+                ':title' => sprintf(__('Task #%s: %s'),
+                    $task->getNumber(),
+                    $task->isAssigned() ? __('Reassign') :  __('Assign')),
+                ':action' => sprintf('#tasks/%d/assign',
+                    $task->getId()),
+                );
+        if ($task->isAssigned()) {
+            $info['notice'] = sprintf(__('%s is currently assigned to %s'),
+                    __('Task'),
+                    $task->getAssigned());
+        }
 
+        $form = $task->getAssignmentForm($_POST);
+        if ($_POST && $form->isValid()) {
+            if ($task->assign($form, $errors)) {
+                $_SESSION['::sysmsgs']['msg'] = sprintf(
+                        __('%s successfully'),
+                        sprintf(
+                            __('%s assigned to %s'),
+                            __('Task'),
+                            $form->getAssignee())
+                        );
+                Http::response(201, $task->getId());
             }
 
-            $info = Format::htmlchars($_POST);
+            $form->addErrors($errors);
             $info['error'] = $errors['err'] ?: __('Unable to assign task');
         }
 
@@ -109,23 +402,34 @@ class TasksAjaxAPI extends AjaxController {
         if (!$task->checkStaffPerm($thisstaff, Task::PERM_DELETE))
             Http::response(403, __('Permission Denied'));
 
-        $info = $errors = array();
+        $errors = array();
+        $info = array(
+                ':title' => sprintf(__('Task #%s: %s'),
+                    $task->getNumber(),
+                    __('Delete')),
+                ':action' => sprintf('#tasks/%d/delete',
+                    $task->getId()),
+                );
+
         if ($_POST) {
             if ($task->delete($_POST,  $errors)) {
+                $_SESSION['::sysmsgs']['msg'] = sprintf(
+                            __('%s #%s deleted successfully'),
+                            __('Task'),
+                            $task->getNumber(),
+                            $task->getDept());
                 Http::response(201, 0);
-
             }
-
-            $info = Format::htmlchars($_POST);
+            $info = array_merge($info, Format::htmlchars($_POST));
             $info['error'] = $errors['err'] ?: __('Unable to delete task');
         }
-        $info['placeholder'] = sprintf(__(
+        $info[':placeholder'] = sprintf(__(
                     'Optional reason for deleting %s'),
                 __('this task'));
         $info['warn'] = sprintf(__(
                     'Are you sure you want to DELETE %s?'),
                     __('this task'));
-        $info['extra'] = sprintf('<strong>%s</strong>',
+        $info[':extra'] = sprintf('<strong>%s</strong>',
                     __('Deleted tasks CANNOT be recovered, including any associated attachments.')
                     );
 
@@ -141,7 +445,7 @@ class TasksAjaxAPI extends AjaxController {
             Http::response(404, __('No such task'));
 
         $info=$errors=array();
-        $task_note_form = new Form(array(
+        $note_form = new SimpleForm(array(
             'attachments' => new FileUploadField(array('id'=>'attach',
             'name'=>'attach:note',
             'configuration' => array('extensions'=>'')))
@@ -152,14 +456,14 @@ class TasksAjaxAPI extends AjaxController {
             switch ($_POST['a']) {
             case 'postnote':
                 $vars = $_POST;
-                $attachments = $task_note_form->getField('attachments')->getClean();
+                $attachments = $note_form->getField('attachments')->getClean();
                 $vars['cannedattachments'] = array_merge(
                     $vars['cannedattachments'] ?: array(), $attachments);
                 if(($note=$task->postNote($vars, $errors, $thisstaff))) {
                     $msg=__('Note posted successfully');
                     // Clear attachment list
-                    $task_note_form->setSource(array());
-                    $task_note_form->getField('attachments')->reset();
+                    $note_form->setSource(array());
+                    $note_form->getField('attachments')->reset();
                     Draft::deleteForNamespace('task.note.'.$task->getId(),
                             $thisstaff->getId());
                 } else {
diff --git a/include/ajax.tickets.php b/include/ajax.tickets.php
index 28ed7d2b4fbfcd6de7af37038c6321d02dc3e628..d1ea440c0495351edb894f76acccb13599d670be 100644
--- a/include/ajax.tickets.php
+++ b/include/ajax.tickets.php
@@ -924,7 +924,8 @@ class TicketsAjaxAPI extends AjaxController {
                     sprintf('ticket.%d.task', $ticket->getId()),
                     $thisstaff->getId());
             // Default form
-            $form = TaskForm::getDefaultForm()->getForm($_POST);
+            $form = TaskForm::getInstance();
+            $form->setSource($_POST);
             // Internal form
             $iform = TaskForm::getInternalForm($_POST);
             $isvalid = true;
@@ -963,5 +964,51 @@ class TicketsAjaxAPI extends AjaxController {
 
          include STAFFINC_DIR . 'templates/task.tmpl.php';
     }
+
+    function task($tid, $id) {
+        global $thisstaff;
+
+        if (!($ticket=Ticket::lookup($tid))
+                || !$ticket->checkStaffPerm($thisstaff))
+            Http::response(404, 'Unknown ticket');
+
+        // Lookup task and check access
+        if (!($task=Task::lookup($id))
+                || !$task->checkStaffPerm($thisstaff))
+            Http::response(404, 'Unknown task');
+
+        $info=$errors=array();
+        $note_form = new SimpleForm(array(
+            'attachments' => new FileUploadField(array('id'=>'attach',
+            'name'=>'attach:note',
+            'configuration' => array('extensions'=>'')))
+            ));
+
+        if ($_POST) {
+            switch ($_POST['a']) {
+            case 'postnote':
+                $vars = $_POST;
+                $attachments = $note_form->getField('attachments')->getClean();
+                $vars['cannedattachments'] = array_merge(
+                    $vars['cannedattachments'] ?: array(), $attachments);
+                if(($note=$task->postNote($vars, $errors, $thisstaff))) {
+                    $msg=__('Note posted successfully');
+                    // Clear attachment list
+                    $note_form->setSource(array());
+                    $note_form->getField('attachments')->reset();
+                    Draft::deleteForNamespace('task.note.'.$task->getId(),
+                            $thisstaff->getId());
+                } else {
+                    if(!$errors['err'])
+                        $errors['err'] = __('Unable to post the note - missing or invalid data.');
+                }
+                break;
+            default:
+                $errors['err'] = __('Unknown action');
+            }
+        }
+
+        include STAFFINC_DIR . 'templates/task-view.tmpl.php';
+    }
 }
 ?>
diff --git a/include/class.config.php b/include/class.config.php
index a2cad713eb487ab4542f4186fb19cc4c5f308fe1..a85fc696263e4316da1f0181e6ccf6a4ea6921ce 100644
--- a/include/class.config.php
+++ b/include/class.config.php
@@ -847,6 +847,88 @@ class OsticketConfig extends Config {
         return ($this->get('overlimit_notice_active'));
     }
 
+    /* Tasks */
+
+    function alertONNewTask() {
+        return ($this->get('task_alert_active'));
+    }
+
+    function alertAdminONNewTask() {
+        return ($this->get('task_alert_admin'));
+    }
+
+    function alertDeptManagerONNewTask() {
+        return ($this->get('task_alert_dept_manager'));
+    }
+
+    function alertDeptMembersONNewTask() {
+        return ($this->get('task_alert_dept_members'));
+    }
+
+    function alertONTaskActivity() {
+        return ($this->get('task_activity_alert_active'));
+    }
+
+    function alertLastRespondentONTaskActivity() {
+        return ($this->get('task_activity_alert_laststaff'));
+    }
+
+    function alertAssignedONTaskActivity() {
+        return ($this->get('task_activity_alert_assigned'));
+    }
+
+    function alertDeptManagerONTaskActivity() {
+        return ($this->get('task_activity_alert_dept_manager'));
+    }
+
+    function alertONTaskTransfer() {
+        return ($this->get('task_transfer_alert_active'));
+    }
+
+    function alertAssignedONTaskTransfer() {
+        return ($this->get('task_transfer_alert_assigned'));
+    }
+
+    function alertDeptManagerONTaskTransfer() {
+        return ($this->get('task_transfer_alert_dept_manager'));
+    }
+
+    function alertDeptMembersONTaskTransfer() {
+        return ($this->get('task_transfer_alert_dept_members'));
+    }
+
+    function alertONTaskAssignment() {
+        return ($this->get('task_assignment_alert_active'));
+    }
+
+    function alertStaffONTaskAssignment() {
+        return ($this->get('task_assignment_alert_staff'));
+    }
+
+    function alertTeamLeadONTaskAssignment() {
+        return ($this->get('task_assignment_alert_team_lead'));
+    }
+
+    function alertTeamMembersONTaskAssignment() {
+        return ($this->get('task_assignment_alert_team_members'));
+    }
+
+    function alertONOverdueTask() {
+        return ($this->get('task_overdue_alert_active'));
+    }
+
+    function alertAssignedONOverdueTask() {
+        return ($this->get('task_overdue_alert_assigned'));
+    }
+
+    function alertDeptManagerONOverdueTask() {
+        return ($this->get('task_overdue_alert_dept_manager'));
+    }
+
+    function alertDeptMembersONOverdueTask() {
+        return ($this->get('task_overdue_alert_dept_members'));
+    }
+
     /* Error alerts sent to admin email when enabled */
     function alertONSQLError() {
         return ($this->get('send_sql_errors'));
@@ -905,6 +987,9 @@ class OsticketConfig extends Config {
             case 'tickets':
                 return $this->updateTicketsSettings($vars, $errors);
                 break;
+            case 'tasks':
+                return $this->updateTasksSettings($vars, $errors);
+                break;
             case 'emails':
                 return $this->updateEmailsSettings($vars, $errors);
                 break;
@@ -1056,6 +1141,82 @@ class OsticketConfig extends Config {
         ));
     }
 
+    function updateTasksSettings($vars, &$errors) {
+        $f=array();
+        $f['default_task_sla_id']=array('type'=>'int',   'required'=>1, 'error'=>__('Selection required'));
+        $f['default_task_priority_id']=array('type'=>'int',   'required'=>1, 'error'=>__('Selection required'));
+
+        if (!preg_match('`(?!<\\\)#`', $vars['task_number_format']))
+            $errors['task_number_format'] = 'Task number format requires at least one hash character (#)';
+
+        Validator::process($f, $vars, $errors);
+
+        if ($vars['task_alert_active']
+                && (!isset($vars['task_alert_admin'])
+                    && !isset($vars['task_alert_dept_manager'])
+                    && !isset($vars['task_alert_dept_members'])
+                    && !isset($vars['task_alert_acct_manager']))) {
+            $errors['task_alert_active'] = __('Select recipient(s)');
+        }
+
+        if ($vars['task_activity_alert_active']
+                && (!isset($vars['task_activity_alert_laststaff'])
+                    && !isset($vars['task_activity_alert_assigned'])
+                    && !isset($vars['task_activity_alert_dept_manager']))) {
+            $errors['task_activity_alert_active'] = __('Select recipient(s)');
+        }
+
+        if ($vars['task_transfer_alert_active']
+                && (!isset($vars['task_transfer_alert_assigned'])
+                    && !isset($vars['task_transfer_alert_dept_manager'])
+                    && !isset($vars['task_transfer_alert_dept_members']))) {
+            $errors['task_transfer_alert_active'] = __('Select recipient(s)');
+        }
+
+        if ($vars['task_overdue_alert_active']
+                && (!isset($vars['task_overdue_alert_assigned'])
+                    && !isset($vars['task_overdue_alert_dept_manager'])
+                    && !isset($vars['task_overdue_alert_dept_members']))) {
+            $errors['task_overdue_alert_active'] = __('Select recipient(s)');
+        }
+
+        if ($vars['task_assignment_alert_active']
+                && (!isset($vars['task_assignment_alert_staff'])
+                    && !isset($vars['task_assignment_alert_team_lead'])
+                    && !isset($vars['task_assignment_alert_team_members']))) {
+            $errors['task_assignment_alert_active'] = __('Select recipient(s)');
+        }
+
+        if ($errors)
+            return false;
+
+        return $this->updateAll(array(
+            'task_number_format'=>$vars['task_number_format'] ?: '######',
+            'task_sequence_id'=>$vars['task_sequence_id'] ?: 0,
+            'default_task_priority_id'=>$vars['default_task_priority_id'],
+            'default_task_sla_id'=>$vars['default_task_sla_id'],
+            'task_alert_active'=>$vars['task_alert_active'],
+            'task_alert_admin'=>isset($vars['task_alert_admin']) ? 1 : 0,
+            'task_alert_dept_manager'=>isset($vars['task_alert_dept_manager']) ? 1 : 0,
+            'task_alert_dept_members'=>isset($vars['task_alert_dept_members']) ? 1 : 0,
+            'task_activity_alert_active'=>$vars['task_activity_alert_active'],
+            'task_activity_alert_laststaff'=>isset($vars['task_activity_alert_laststaff']) ? 1 : 0,
+            'task_activity_alert_assigned'=>isset($vars['task_activity_alert_assigned']) ? 1 : 0,
+            'task_activity_alert_dept_manager'=>isset($vars['task_activity_alert_dept_manager']) ? 1 : 0,
+            'task_assignment_alert_active'=>$vars['task_assignment_alert_active'],
+            'task_assignment_alert_staff'=>isset($vars['task_assignment_alert_staff']) ? 1 : 0,
+            'task_assignment_alert_team_lead'=>isset($vars['task_assignment_alert_team_lead']) ? 1 : 0,
+            'task_assignment_alert_team_members'=>isset($vars['task_assignment_alert_team_members']) ? 1 : 0,
+            'task_transfer_alert_active'=>$vars['task_transfer_alert_active'],
+            'task_transfer_alert_assigned'=>isset($vars['task_transfer_alert_assigned']) ? 1 : 0,
+            'task_transfer_alert_dept_manager'=>isset($vars['task_transfer_alert_dept_manager']) ? 1 : 0,
+            'task_transfer_alert_dept_members'=>isset($vars['task_transfer_alert_dept_members']) ? 1 : 0,
+            'task_overdue_alert_active'=>$vars['task_overdue_alert_active'],
+            'task_overdue_alert_assigned'=>isset($vars['task_overdue_alert_assigned']) ? 1 : 0,
+            'task_overdue_alert_dept_manager'=>isset($vars['task_overdue_alert_dept_manager']) ? 1 : 0,
+            'task_overdue_alert_dept_members'=>isset($vars['task_overdue_alert_dept_members']) ? 1 : 0,
+        ));
+    }
 
     function updateEmailsSettings($vars, &$errors) {
         $f=array();
diff --git a/include/class.dept.php b/include/class.dept.php
index 43a98692d1af69cf0c67e2dd3d90dac30d0741ae..8343d6f6690c9917a8fbe8f4e145d7a5f08e03bd 100644
--- a/include/class.dept.php
+++ b/include/class.dept.php
@@ -175,6 +175,7 @@ implements TemplateVariable {
                     'onvacation' => 0,
                 ));
 
+            $members->distinct('staff_id');
             switch ($cfg->getDefaultNameFormat()) {
             case 'last':
             case 'lastfirst':
diff --git a/include/class.dynamic_forms.php b/include/class.dynamic_forms.php
index e569f83792ae45880ee428c63b961e40d63cedf8..c2c90a3a78510b85886801992bcaf6f43af2321d 100644
--- a/include/class.dynamic_forms.php
+++ b/include/class.dynamic_forms.php
@@ -57,6 +57,10 @@ class DynamicForm extends VerySimpleModel {
         return $base;
     }
 
+    function getId() {
+        return $this->id;
+    }
+
     /**
      * Fetch a list of field implementations for the fields defined in this
      * form. This method should *always* be preferred over
@@ -112,7 +116,7 @@ class DynamicForm extends VerySimpleModel {
         if ($source)
             $this->reset();
         $fields = $this->getFields();
-        $form = new Form($fields, $source, array(
+        $form = new SimpleForm($fields, $source, array(
             'title' => $this->getLocal('title'),
             'instructions' => $this->getLocal('instructions'))
         );
@@ -254,6 +258,87 @@ class DynamicForm extends VerySimpleModel {
         return true;
     }
 
+    static function ensureDynamicDataView() {
+
+        if (!($cdata=static::$cdata) || !$cdata['table'])
+            return false;
+
+        $sql = 'SHOW TABLES LIKE \''.$cdata['table'].'\'';
+        if (!db_num_rows(db_query($sql)))
+            return static::buildDynamicDataView($cdata);
+    }
+
+    static function buildDynamicDataView($cdata) {
+        $sql = 'CREATE TABLE `'.$cdata['table'].'` (PRIMARY KEY
+                ('.$cdata['object_id'].')) AS '
+             .  static::getCrossTabQuery( $cdata['object_type'], $cdata['object_id']);
+        db_query($sql);
+    }
+
+    static function dropDynamicDataView($table) {
+        db_query('DROP TABLE IF EXISTS `'.$table.'`');
+    }
+
+    static function updateDynamicDataView($answer, $data) {
+        // TODO: Detect $data['dirty'] for value and value_id
+        // We're chiefly concerned with Ticket form answers
+
+        $cdata = static::$cdata;
+        if (!$cdata
+                || !$cdata['table']
+                || !($e = $answer->getEntry())
+                || $e->form->get('type') != $cdata['object_type'])
+            return;
+
+        // $record = array();
+        // $record[$f] = $answer->value'
+        // TicketFormData::objects()->filter(array('ticket_id'=>$a))
+        //      ->merge($record);
+        $sql = 'SHOW TABLES LIKE \''.$cdata['table'].'\'';
+        if (!db_num_rows(db_query($sql)))
+            return;
+
+        $f = $answer->getField();
+        $name = $f->get('name') ? $f->get('name')
+            : 'field_'.$f->get('id');
+        $fields = sprintf('`%s`=', $name) . db_input(
+            implode(',', $answer->getSearchKeys()));
+        $sql = 'INSERT INTO `'.$cdata['table'].'` SET '.$fields
+            . sprintf(', `%s`= %s',
+                    $cdata['object_id'],
+                    db_input($answer->getEntry()->get('object_id')))
+            .' ON DUPLICATE KEY UPDATE '.$fields;
+        if (!db_query($sql) || !db_affected_rows())
+            return self::dropDynamicDataView($cdata['table']);
+    }
+
+    static function updateDynamicFormEntryAnswer($answer, $data) {
+        if (!$answer
+                || !($e = $answer->getEntry())
+                || !$e->form)
+            return;
+
+        switch ($e->form->get('type')) {
+        case 'T':
+            return TicketForm::updateDynamicDataView($answer, $data);
+        case 'A':
+            return TaskForm::updateDynamicDataView($answer, $data);
+        }
+
+    }
+
+    static function updateDynamicFormField($field, $data) {
+        if (!$field || !$field->form)
+            return;
+
+        switch ($field->form->get('type')) {
+        case 'T':
+            return TicketForm::dropDynamicDataView(TicketForm::$cdata['table']);
+        case 'A':
+            return TaskForm::dropDynamicDataView(TaskForm::$cdata['table']);
+        }
+
+    }
 
     static function getCrossTabQuery($object_type, $object_id='object_id', $exclude=array()) {
         $fields = static::getDynamicDataViewFields($exclude);
@@ -264,8 +349,7 @@ class DynamicForm extends VerySimpleModel {
             WHERE entry.object_type='$object_type' GROUP BY entry.object_id";
     }
 
-    // Materialized View for Ticket custom data (MySQL FlexViews would be
-    // nice)
+    // Materialized View for custom data (MySQL FlexViews would be nice)
     //
     // @see http://code.google.com/p/flexviews/
     static function getDynamicDataViewFields($exclude) {
@@ -347,6 +431,12 @@ Filter::addSupportedMatches(/* @trans */ 'User Data', function() {
 class TicketForm extends DynamicForm {
     static $instance;
 
+    static $cdata = array(
+            'table' => TICKET_CDATA_TABLE,
+            'object_id' => 'ticket_id',
+            'object_type' => 'T',
+        );
+
     static function objects() {
         $os = parent::objects();
         return $os->filter(array('type'=>'T'));
@@ -435,27 +525,24 @@ Filter::addSupportedMatches(/* @trans */ 'Ticket Data', function() {
 }, 30);
 // Manage materialized view on custom data updates
 Signal::connect('model.created',
-    array('TicketForm', 'updateDynamicDataView'),
+    array('DynamicForm', 'updateDynamicFormEntryAnswer'),
     'DynamicFormEntryAnswer');
 Signal::connect('model.updated',
-    array('TicketForm', 'updateDynamicDataView'),
+    array('DynamicForm', 'updateDynamicFormEntryAnswer'),
     'DynamicFormEntryAnswer');
 // Recreate the dynamic view after new or removed fields to the ticket
 // details form
 Signal::connect('model.created',
-    array('TicketForm', 'dropDynamicDataView'),
-    'DynamicFormField',
-    function($o) { return $o->form->type == 'T'; });
+    array('DynamicForm', 'updateDynamicFormField'),
+    'DynamicFormField');
 Signal::connect('model.deleted',
-    array('TicketForm', 'dropDynamicDataView'),
-    'DynamicFormField',
-    function($o) { return $o->form->type == 'T'; });
+    array('DynamicForm', 'updateDynamicFormField'),
+    'DynamicFormField');
 // If the `name` column is in the dirty list, we would be renaming a
 // column. Delete the view instead.
 Signal::connect('model.updated',
-    array('TicketForm', 'dropDynamicDataView'),
+    array('DynamicForm', 'updateDynamicFormField'),
     'DynamicFormField',
-    // TODO: Lookup the dynamic form to verify {type == 'T'}
     function($o, $d) { return isset($d['dirty'])
         && (isset($d['dirty']['name']) || isset($d['dirty']['type'])); });
 
@@ -898,7 +985,7 @@ class DynamicFormEntry extends VerySimpleModel {
     function getForm() {
         if (!isset($this->_form)) {
             // XXX: Should source be $this?
-            $form = new Form($this->getFields(), $this->getSource(),
+            $form = new SimpleForm($this->getFields(), $this->getSource(),
             array(
                 'title' => $this->getTitle(),
                 'instructions' => $this->getInstructions(),
diff --git a/include/class.filter_action.php b/include/class.filter_action.php
index 7de00efe9e82d79e974d3bd2411c60161920baf6..fc3f356f7495342c633429e1bdb2cfa151bfe3b9 100644
--- a/include/class.filter_action.php
+++ b/include/class.filter_action.php
@@ -138,7 +138,7 @@ abstract class TriggerAction {
             foreach ($options as $f) {
                 $f->set('id', $uid++);
             }
-            $this->_cform = new Form($options, $source);
+            $this->_cform = new SimpleForm($options, $source);
             if (!$source) {
                 foreach ($this->_cform->getFields() as $name=>$f) {
                     if ($config && isset($config[$name]))
diff --git a/include/class.forms.php b/include/class.forms.php
index 59b2d7251b23155e2c9dad61465a88c006415e44..75acdbd92a89fca54b6c751fad420b7384f0d32f 100644
--- a/include/class.forms.php
+++ b/include/class.forms.php
@@ -19,6 +19,9 @@
  * data for a ticket
  */
 class Form {
+
+    static $id = 0;
+
     var $fields = array();
     var $title = '';
     var $instructions = '';
@@ -28,26 +31,42 @@ class Form {
     var $_errors = null;
     var $_source = false;
 
-    function __construct($fields=array(), $source=null, $options=array()) {
-        $this->fields = $fields;
-        foreach ($fields as $k=>$f) {
-            $f->setForm($this);
-            if (!$f->get('name') && $k && !is_numeric($k))
-                $f->set('name', $k);
-        }
+    function __construct($source=null, $options=array()) {
+
         if (isset($options['title']))
             $this->title = $options['title'];
         if (isset($options['instructions']))
             $this->instructions = $options['instructions'];
+        if (isset($options['id']))
+            $this->id = $options['id'];
+
         // Use POST data if source was not specified
         $this->_source = ($source) ? $source : $_POST;
     }
+
+    function getId() {
+        return static::$id;
+    }
+
     function data($source) {
         foreach ($this->fields as $name=>$f)
             if (isset($source[$name]))
                 $f->value = $source[$name];
     }
 
+    function setFields($fields) {
+
+        if (!is_array($fields))
+            return;
+
+        $this->fields = $fields;
+        foreach ($fields as $k=>$f) {
+            $f->setForm($this);
+            if (!$f->get('name') && $k && !is_numeric($k))
+                $f->set('name', $k);
+        }
+    }
+
     function getFields() {
         return $this->fields;
     }
@@ -120,8 +139,21 @@ class Form {
         return ($formOnly) ? $this->_errors['form'] : $this->_errors;
     }
 
-    function addError($message) {
-        $this->_errors['form'][] = $message;
+    function addError($message, $index=false) {
+
+        if ($index)
+            $this->_errors[$index] = $message;
+        else
+            $this->_errors['form'][] = $message;
+    }
+
+    function addErrors($errors=array()) {
+        foreach ($errors as $k => $v) {
+            if (($f=$this->getField($k)))
+                $f->addError($v);
+            else
+                $this->addError($v, $k);
+        }
     }
 
     function addValidator($function) {
@@ -163,6 +195,42 @@ class Form {
         }
     }
 
+    function emitJavascript($options=array()) {
+
+        // Check if we need to emit javascript
+        if (!($fid=$this->getId()))
+            return;
+        ?>
+        <script type="text/javascript">
+          $(function() {
+            <?php
+            //XXX: We ONLY want to watch field on this form. We'll only
+            // watch form inputs if form_id is specified. Current FORM API
+            // doesn't generate the entire form  (just fields)
+            if ($fid) {
+                ?>
+                $(document).off('change.<?php echo $fid; ?>');
+                $(document).on('change.<?php echo $fid; ?>',
+                    'form#<?php echo $fid; ?> :input',
+                    function() {
+                        //Clear any current errors...
+                        var errors = $('#field'+$(this).attr('id')+'_error');
+                        if (errors.length)
+                            errors.slideUp('fast', function (){
+                                $(this).remove();
+                                });
+                        //TODO: Validation input inplace or via ajax call
+                        // and set any new errors AND visibilty changes
+                    }
+                   );
+            <?php
+            }
+            ?>
+            });
+        </script>
+        <?php
+    }
+
     static function emitMedia($url, $type) {
         if ($url[0] == '/')
             $url = ROOT_PATH . substr($url, 1);
@@ -221,6 +289,28 @@ class Form {
             }
         }
     }
+
+    /*
+     * Initialize a generic static form
+     */
+    static function instantiate() {
+        $r = new ReflectionClass(get_called_class());
+        return $r->newInstanceArgs(func_get_args());
+    }
+}
+
+/**
+ * SimpleForm
+ * Wrapper for inline/static forms.
+ *
+ */
+class SimpleForm extends Form {
+
+    function __construct($fields=array(), $source=null, $options=array()) {
+        parent::__construct($source, $options);
+        $this->setFields($fields);
+    }
+
 }
 
 require_once(INCLUDE_DIR . "class.json.php");
@@ -340,14 +430,22 @@ class FormField {
         $this->_clean = $this->_widget = null;
     }
 
+    function getValue() {
+        return $this->getWidget()->getValue();
+    }
+
     function errors() {
         return $this->_errors;
     }
-    function addError($message, $field=false) {
+    function addError($message, $index=false) {
         if ($field)
-            $this->_errors[$field] = $message;
+            $this->_errors[$index] = $message;
         else
             $this->_errors[] = $message;
+
+        // Update parent form errors for the field
+        if ($this->_form)
+            $this->_form->addError($this->errors(), $this->get('id'));
     }
 
     function isValidEntry() {
@@ -818,7 +916,7 @@ class FormField {
             $clazz = $type[1];
             $T = new $clazz($this->ht);
             $config = $this->getConfiguration();
-            $this->_cform = new Form($T->getConfigurationOptions(), $source);
+            $this->_cform = new SimpleForm($T->getConfigurationOptions(), $source);
             if (!$source) {
                 foreach ($this->_cform->getFields() as $name=>$f) {
                     if ($config && isset($config[$name]))
@@ -1008,7 +1106,7 @@ class TextareaField extends FormField {
     }
 
     function searchable($value) {
-        $value = preg_replace(array('`<br(\s*)?/?>`i', '`</div>`i'), "\n", $value);
+        $value = preg_replace(array('`<br(\s*)?/?>`i', '`</div>`i'), "\n", $value); //<?php
         $value = Format::htmldecode(Format::striptags($value));
         return Format::searchable($value);
     }
@@ -1138,6 +1236,7 @@ class BooleanField extends FormField {
 
 class ChoiceField extends FormField {
     static $widget = 'ChoicesWidget';
+    var $_choices;
 
     function getConfigurationOptions() {
         return array(
@@ -1213,6 +1312,18 @@ class ChoiceField extends FormField {
         return (string) $value;
     }
 
+    /*
+     Return criteria to which the choice should be filtered by
+     */
+    function getCriteria() {
+        $config = $this->getConfiguration();
+        $criteria = array();
+        if (isset($config['criteria']))
+            $criteria = $config['criteria'];
+
+        return $criteria;
+    }
+
     function getChoice($value) {
 
         $choices = $this->getChoices();
@@ -1693,6 +1804,9 @@ FormField::addFieldTypes(/*@trans*/ 'Dynamic Fields', function() {
 
 
 class AssigneeField extends ChoiceField {
+    var $_choices = array();
+    var $_criteria = null;
+
     function getWidget() {
         $widget = parent::getWidget();
         if (is_object($widget->value))
@@ -1700,20 +1814,62 @@ class AssigneeField extends ChoiceField {
         return $widget;
     }
 
+    function getCriteria() {
+
+        if (!isset($this->_criteria)) {
+            $this->_criteria = array('available' => true);
+            if (($c=parent::getCriteria()))
+                $this->_criteria = array_merge($this->_criteria, $c);
+        }
+
+        return $this->_criteria;
+    }
+
     function hasIdValue() {
         return true;
     }
 
     function getChoices() {
         global $cfg;
-        $choices = array();
-        if (($agents = Staff::getAvailableStaffMembers()))
+
+        if (!$this->_choices) {
+            $config = $this->getConfiguration();
+            $choices = array(
+                    __('Agents') => new ArrayObject(),
+                    __('Teams') => new ArrayObject());
+            $A = current($choices);
+            $criteria = $this->getCriteria();
+            $agents = array();
+            if (($dept=$config['dept']) && $dept->assignMembersOnly()) {
+                if (($members = $dept->getMembers($criteria)))
+                    foreach ($members as $member)
+                        $agents[$member->getId()] = $member;
+            } else {
+                $agents = Staff::getStaffMembers($criteria);
+            }
+
             foreach ($agents as $id => $name)
-                $choices[$id] = $name;
+                $A['s'.$id] = $name;
 
-        return $choices;
+            next($choices);
+            $T = current($choices);
+            if (($teams = Team::getTeams()))
+                foreach ($teams as $id => $name)
+                    $T['t'.$id] = $name;
+
+            $this->_choices = $choices;
+        }
+
+        return $this->_choices;
+    }
+
+    function getValue() {
+
+        if (($value = parent::getValue()) && ($id=$this->getClean()))
+           return $value[$id];
     }
 
+
     function parse($id) {
         return $this->to_php(null, $id);
     }
@@ -2291,7 +2447,7 @@ class InlineFormField extends FormField {
     function getInlineForm($data=false) {
         $form = $this->get('form');
         if (is_array($form)) {
-            $form = new Form($form, $data ?: $this->value ?: $this->getSource());
+            $form = new SimpleForm($form, $data ?: $this->value ?: $this->getSource());
         }
         return $form;
     }
@@ -2573,14 +2729,9 @@ class ChoicesWidget extends Widget {
             <?php if (!$have_def && !$config['multiselect']) { ?>
             <option value="<?php echo $def_key; ?>">&mdash; <?php
                 echo $def_val; ?> &mdash;</option>
-            <?php }
-            foreach ($choices as $key => $name) {
-                if (!$have_def && $key == $def_key)
-                    continue; ?>
-                <option value="<?php echo $key; ?>" <?php
-                    if (isset($values[$key])) echo 'selected="selected"';
-                ?>><?php echo $name; ?></option>
-            <?php } ?>
+<?php
+        }
+        $this->emitChoices($choices, $values); ?>
         </select>
         <?php
         if ($config['multiselect']) {
@@ -2595,6 +2746,35 @@ class ChoicesWidget extends Widget {
         }
     }
 
+    function emitChoices($choices, $values=array()) {
+        reset($choices);
+        if (is_array(current($choices)) || current($choices) instanceof Traversable)
+            return $this->emitComplexChoices($choices, $values);
+
+        foreach ($choices as $key => $name) {
+            if (!$have_def && $key == $def_key)
+                continue; ?>
+            <option value="<?php echo $key; ?>" <?php
+                if (isset($values[$key])) echo 'selected="selected"';
+            ?>><?php echo $name; ?></option>
+        <?php
+        }
+    }
+
+    function emitComplexChoices($choices, $values=array()) {
+        foreach ($choices as $label => $group) { ?>
+            <optgroup label="<?php echo $label; ?>"><?php
+            foreach ($group as $key => $name) {
+                if (!$have_def && $key == $def_key)
+                    continue; ?>
+            <option value="<?php echo $key; ?>" <?php
+                if (isset($values[$key])) echo 'selected="selected"';
+            ?>><?php echo $name; ?></option>
+<?php       } ?>
+            </optgroup><?php
+        }
+    }
+
     function getValue() {
 
         if (!($value = parent::getValue()))
@@ -2606,12 +2786,23 @@ class ChoicesWidget extends Widget {
         // Assume multiselect
         $values = array();
         $choices = $this->field->getChoices();
-        if (is_array($value)) {
-            foreach($value as $k => $v) {
-                if (isset($choices[$v]))
-                    $values[$v] = $choices[$v];
-                elseif (($i=$this->field->lookupChoice($v)))
-                    $values += $i;
+
+        if ($choices && is_array($value)) {
+            // Complex choices
+            if (is_array(current($choices))
+                    || current($choices) instanceof Traversable) {
+                foreach ($choices as $label => $group) {
+                     foreach ($group as $k => $v)
+                        if (in_array($k, $value))
+                            $values[$k] = $v;
+                }
+            } else {
+                foreach($value as $k => $v) {
+                    if (isset($choices[$v]))
+                        $values[$v] = $choices[$v];
+                    elseif (($i=$this->field->lookupChoice($v)))
+                        $values += $i;
+                }
             }
         }
 
@@ -3056,4 +3247,203 @@ class VisibilityConstraint {
     }
 }
 
+class AssignmentForm extends Form {
+
+    static $id = 'assign';
+    var $_assignee = null;
+    var $_dept = null;
+
+    function __construct($source=null, $options=array()) {
+        parent::__construct($source, $options);
+        // Department of the object -- if necessary to limit assinees
+        if (isset($options['dept']))
+            $this->_dept = $options['dept'];
+    }
+
+    function getFields() {
+
+        if ($this->fields)
+            return $this->fields;
+
+        $fields = array(
+            'assignee' => new AssigneeField(array(
+                    'id'=>1,
+                    'label' => __('Assignee'),
+                    'flags' => hexdec(0X450F3),
+                    'required' => true,
+                    'validator-error' => __('Assignee selection required'),
+                    'configuration' => array(
+                        'criteria' => array(
+                            'available' => true,
+                            ),
+                        'dept' => $this->_dept ?: null,
+                       ),
+                    )
+                ),
+            'comments' => new TextareaField(array(
+                    'id' => 2,
+                    'label'=> '',
+                    'required'=>false,
+                    'default'=>'',
+                    'configuration' => array(
+                        'html' => true,
+                        'size' => 'large',
+                        'placeholder' => __('Optional reason for the assignment'),
+                        ),
+                    )
+                ),
+            );
+
+        $this->setFields($fields);
+
+        return $this->fields;
+    }
+
+    function isValid() {
+
+        if (!parent::isValid())
+            return false;
+
+        // Do additional assignment validation
+        if (!($assignee = $this->getAssignee())) {
+            $this->getField('assignee')->addError(
+                    __('Unknown assignee'));
+        } elseif ($assignee instanceof Staff) {
+            // Make sure the agent is available
+            if (!$assignee->isAvailable())
+                $this->getField('assignee')->addError(
+                        __('Agent is unavailable for assignment')
+                        );
+        }
+
+        return !$this->errors();
+    }
+
+    function getClean() {
+        return parent::getClean();
+    }
+
+    function render($options) {
+
+        switch(strtolower($options['template'])) {
+        case 'simple':
+            $inc = STAFFINC_DIR . 'templates/dynamic-form-simple.tmpl.php';
+            break;
+        default:
+            throw new Exception(sprintf(__('%s: Unknown template style %s'),
+                        'FormUtils', $options['template']));
+        }
+
+        $form = $this;
+        include $inc;
+    }
+
+    function getAssignee() {
+
+        if (!isset($this->_assignee)) {
+            $value = $this->getField('assignee')->getClean();
+            if ($value[0] == 's')
+                $this->_assignee = Staff::lookup(substr($value, 1));
+            elseif ($value[0] == 't')
+                $this->_assignee = Team::lookup(substr($value, 1));
+        }
+
+        return $this->_assignee;
+    }
+
+    function assigneeCriteria() {
+        $dept = $this->id;
+        return function () use($dept) {
+            return array('dept_id' =>$dept);
+        };
+    }
+}
+
+class TransferForm extends Form {
+
+    static $id = 'transfer';
+    var $_dept = null;
+
+    function __construct($source=null, $options=array()) {
+        parent::__construct($source, $options);
+    }
+
+    function getFields() {
+
+        if ($this->fields)
+            return $this->fields;
+
+        $fields = array(
+            'dept' => new DepartmentField(array(
+                    'id'=>1,
+                    'label' => __('Department'),
+                    'flags' => hexdec(0X450F3),
+                    'required' => true,
+                    'validator-error' => __('Department selection required'),
+                    )
+                ),
+            'comments' => new TextareaField(array(
+                    'id' => 2,
+                    'label'=> '',
+                    'required'=>false,
+                    'default'=>'',
+                    'configuration' => array(
+                        'html' => true,
+                        'size' => 'small',
+                        'placeholder' => __('Optional reason for the transfer'),
+                        ),
+                    )
+                ),
+            );
+
+        $this->setFields($fields);
+
+        return $this->fields;
+    }
+
+    function isValid() {
+
+        if (!parent::isValid())
+            return false;
+
+        // Do additional validations
+        if (!($dept = $this->getDept()))
+            $this->getField('dept')->addError(
+                    __('Unknown department'));
+
+        return !$this->errors();
+    }
+
+    function getClean() {
+        return parent::getClean();
+    }
+
+    function render($options) {
+
+        switch(strtolower($options['template'])) {
+        case 'simple':
+            $inc = STAFFINC_DIR . 'templates/dynamic-form-simple.tmpl.php';
+            break;
+        default:
+            throw new Exception(sprintf(__('%s: Unknown template style %s'),
+                        get_class(), $options['template']));
+        }
+
+        $form = $this;
+        include $inc;
+
+    }
+
+    function getDept() {
+
+        if (!isset($this->_dept)) {
+            if (($id = $this->getField('dept')->getClean()))
+                $this->_dept = Dept::lookup($id);
+        }
+
+        return $this->_dept;
+    }
+}
+
+
 ?>
diff --git a/include/class.nav.php b/include/class.nav.php
index 6dee08171b05fbf4c7fb7c6eb7658980b89c641a..7d3c119b753d515ddf3e24a76359627eec9723c2 100644
--- a/include/class.nav.php
+++ b/include/class.nav.php
@@ -123,7 +123,9 @@ class StaffNav {
                     'desc' => __('Users'), 'href' => 'users.php', 'title' => __('User Directory')
                 );
             }
+            $this->tabs['tasks'] = array('desc'=>__('Tasks'), 'href'=>'tasks.php', 'title'=>__('Task Queue'));
             $this->tabs['tickets'] = array('desc'=>__('Tickets'),'href'=>'tickets.php','title'=>__('Ticket Queue'));
+
             $this->tabs['kbase'] = array('desc'=>__('Knowledgebase'),'href'=>'kb.php','title'=>__('Knowledgebase'));
             if (count($this->getRegisteredApps()))
                 $this->tabs['apps']=array('desc'=>__('Applications'),'href'=>'apps.php','title'=>__('Applications'));
@@ -140,6 +142,9 @@ class StaffNav {
         foreach($this->getTabs() as $k=>$tab){
             $subnav=array();
             switch(strtolower($k)){
+                case 'tasks':
+                    $subnav[]=array('desc'=>__('Tasks'), 'href'=>'tasks.php', 'iconclass'=>'Ticket', 'droponly'=>true);
+                    break;
                 case 'tickets':
                     $subnav[]=array('desc'=>__('Tickets'),'href'=>'tickets.php','iconclass'=>'Ticket', 'droponly'=>true);
                     if($staff) {
@@ -241,6 +246,7 @@ class AdminNav extends StaffNav{
                     $subnav[]=array('desc'=>__('Company'),'href'=>'settings.php?t=pages','iconclass'=>'pages');
                     $subnav[]=array('desc'=>__('System'),'href'=>'settings.php?t=system','iconclass'=>'preferences');
                     $subnav[]=array('desc'=>__('Tickets'),'href'=>'settings.php?t=tickets','iconclass'=>'ticket-settings');
+                    $subnav[]=array('desc'=>__('Tasks'),'href'=>'settings.php?t=tasks','iconclass'=>'lists');
                     $subnav[]=array('desc'=>__('Access'),'href'=>'settings.php?t=access','iconclass'=>'users');
                     $subnav[]=array('desc'=>__('Knowledgebase'),'href'=>'settings.php?t=kb','iconclass'=>'kb-settings');
                     $subnav[]=array('desc'=>__('Autoresponder'),'href'=>'settings.php?t=autoresp','iconclass'=>'email-autoresponders');
diff --git a/include/class.plugin.php b/include/class.plugin.php
index 4df5688435dc2eb1ae63c1c9cbdac1a1f3c8ffeb..c71d86524df95d44be3ebd3f598029bc17eb7074 100644
--- a/include/class.plugin.php
+++ b/include/class.plugin.php
@@ -32,7 +32,7 @@ class PluginConfig extends Config {
      */
     function getForm() {
         if (!isset($this->form)) {
-            $this->form = new Form($this->getOptions());
+            $this->form = new SimpleForm($this->getOptions());
             if ($_SERVER['REQUEST_METHOD'] != 'POST')
                 $this->form->data($this->getInfo());
         }
diff --git a/include/class.search.php b/include/class.search.php
index 7225f652e7ab3397ca79fcd6c687013c0b313733..d41f0a59a3a6a871fef236d018cbdde2d3555ffb 100644
--- a/include/class.search.php
+++ b/include/class.search.php
@@ -630,7 +630,7 @@ class SavedSearch extends VerySimpleModel {
             $fields = array_merge($fields, self::getSearchField($field, $name));
         }
 
-        $form = new Form($fields, $source);
+        $form = new SimpleForm($fields, $source);
         $form->addValidator(function($form) {
             $selected = 0;
             foreach ($form->getFields() as $F) {
diff --git a/include/class.staff.php b/include/class.staff.php
index 9e093d7fcc93dcf349b1761f1bacd1a77ef2d839..b295a15e194272d0e23ca1284f7bf4eec60ef4c5 100644
--- a/include/class.staff.php
+++ b/include/class.staff.php
@@ -490,6 +490,22 @@ implements AuthenticatedUser, EmailContact, TemplateVariable {
         return ($stats=$this->getTicketsStats())?$stats['closed']:0;
     }
 
+    function getTasksStats() {
+
+        if (!$this->stats['tasks'])
+            $this->stats['tasks'] = Task::getStaffStats($this);
+
+        return  $this->stats['tasks'];
+    }
+
+    function getNumAssignedTasks() {
+        return ($stats=$this->getTasksStats()) ? $stats['assigned'] : 0;
+    }
+
+    function getNumClosedTasks() {
+        return ($stats=$this->getTasksStats()) ? $stats['closed'] : 0;
+    }
+
     function getExtraAttr($attr=false, $default=null) {
         if (!isset($this->_extra) && isset($this->extra))
             $this->_extra = JsonDataParser::decode($this->extra);
@@ -681,11 +697,12 @@ implements AuthenticatedUser, EmailContact, TemplateVariable {
             return null;
     }
 
-    static function getStaffMembers($availableonly=false) {
+    static function getStaffMembers($criteria=array()) {
         global $cfg;
+
         $members = static::objects();
 
-        if ($availableonly) {
+        if (isset($criteria['available'])) {
             $members = $members->filter(array(
                 'group__flags__hasbit' => Group::FLAG_ENABLED,
                 'onvacation' => 0,
@@ -713,7 +730,7 @@ implements AuthenticatedUser, EmailContact, TemplateVariable {
     }
 
     static function getAvailableStaffMembers() {
-        return self::getStaffMembers(true);
+        return self::getStaffMembers(array('available'=>true));
     }
 
     static function getIdByUsername($username) {
diff --git a/include/class.task.php b/include/class.task.php
index a39c28a00ceecbc306086a94bf0afe3dba85c4df..c629379f6e88470408ab6eab9e36467576a38958 100644
--- a/include/class.task.php
+++ b/include/class.task.php
@@ -33,6 +33,22 @@ class TaskModel extends VerySimpleModel {
                 'constraint' => array('staff_id' => 'Staff.staff_id'),
                 'null' => true,
             ),
+            'team' => array(
+                'constraint' => array('team_id' => 'Team.team_id'),
+                'null' => true,
+            ),
+            'thread' => array(
+                'constraint' => array(
+                    'id'  => 'TaskThread.object_id',
+                    "'A'" => 'TaskThread.object_type',
+                ),
+                'list' => false,
+                'null' => false,
+            ),
+            'cdata' => array(
+                'constraint' => array('id' => 'TaskCData.task_id'),
+                'list' => false,
+            ),
         ),
     );
 
@@ -104,14 +120,26 @@ class TaskModel extends VerySimpleModel {
         return $this->staff_id;
     }
 
+    function getStaff() {
+        return $this->staff;
+    }
+
     function getTeamId() {
         return $this->team_id;
     }
 
+    function getTeam() {
+        return $this->team;
+    }
+
     function getDeptId() {
         return $this->dept_id;
     }
 
+    function getDept() {
+        return $this->dept;
+    }
+
     function getCreateDate() {
         return $this->created;
     }
@@ -156,13 +184,12 @@ RolePermission::register(/* @trans */ 'Tasks', TaskModel::getPermissions());
 class Task extends TaskModel {
     var $form;
     var $entry;
-    var $thread;
 
+    var $_thread;
     var $_entries;
 
-
     function getStatus() {
-        return $this->isOpen() ? _('Open') : _('Closed');
+        return $this->isOpen() ? __('Open') : __('Completed');
     }
 
     function getTitle() {
@@ -195,6 +222,34 @@ class Task extends TaskModel {
         return $role->hasPerm($perm);
     }
 
+    function getAssignee() {
+
+        if (!$this->isOpen() || !$this->isAssigned())
+            return false;
+
+        if ($this->staff)
+            return $this->staff;
+
+        if ($this->team)
+            return $this->team;
+
+        return null;
+    }
+
+    function getAssigneeId() {
+
+        if (!($assignee=$this->getAssignee()))
+            return null;
+
+        $id = '';
+        if ($assignee instanceof Staff)
+            $id = 's'.$assignee->getId();
+        elseif ($assignee instanceof Team)
+            $id = 't'.$assignee->getId();
+
+        return $id;
+    }
+
     function getAssignees() {
 
         $assignees=array();
@@ -202,7 +257,7 @@ class Task extends TaskModel {
             $assignees[] = $this->staff->getName();
 
         //Add team assignment
-        if (isset($this->team))
+        if ($this->team)
             $assignees[] = $this->team->getName();
 
         return $assignees;
@@ -215,13 +270,6 @@ class Task extends TaskModel {
     }
 
     function getThread() {
-
-        if (!$this->thread)
-            $this->thread = TaskThread::lookup(array(
-                        'object_id' => $this->getId(),
-                        'object_type' => ObjectModel::OBJECT_TYPE_TASK)
-                    );
-
         return $this->thread;
     }
 
@@ -257,6 +305,23 @@ class Task extends TaskModel {
         return $this->form;
     }
 
+    function getAssignmentForm($source=null) {
+
+        if (!$source)
+            $source = array('assignee' => array($this->getAssigneeId()));
+
+        return AssignmentForm::instantiate($source,
+                array('dept' => $this->getDept()));
+    }
+
+    function getTransferForm($source=null) {
+
+        if (!$source)
+            $source = array('dept' => array($this->getDeptId()));
+
+        return TransferForm::instantiate($source);
+    }
+
     function addDynamicData($data) {
 
         $tf = TaskForm::getInstance($this->id, true);
@@ -283,6 +348,40 @@ class Task extends TaskModel {
         return $this->_entries ?: array();
     }
 
+    function setStatus($status, $comments='') {
+        global $thisstaff;
+
+        switch($status) {
+        case 'open':
+            if ($this->isOpen())
+                return false;
+
+            $this->reopen();
+            break;
+        case 'closed':
+            if ($this->isClosed())
+                return false;
+            $this->close();
+            break;
+        default:
+            return false;
+        }
+
+        $this->save(true);
+        if ($comments) {
+            $errors = array();
+            $this->postNote(array(
+                        'note' => $comments,
+                        'title' => sprintf(
+                            __('Status changed to %s'),
+                            $this->getStatus())
+                        ),
+                    $errors,
+                    $thisstaff);
+        }
+
+        return true;
+    }
 
     function to_json() {
 
@@ -298,13 +397,13 @@ class Task extends TaskModel {
 
         foreach ($this->getDynamicData() as $e) {
             // Make sure the form type matches
-            if (!$e->getForm()
-                    || ($ftype && $ftype != $e->getForm()->get('type')))
+            if (!$e->form
+                    || ($ftype && $ftype != $e->form->get('type')))
                 continue;
 
             // Get the named field and return the answer
-            if ($f = $e->getForm()->getField($field))
-                return $f->getAnswer();
+            if ($a = $e->getAnswer($field))
+                return $a;
         }
 
         return null;
@@ -315,44 +414,82 @@ class Task extends TaskModel {
     }
 
     /* util routines */
-    function assign($vars, &$errors) {
-        global $thisstaff;
+    function assign(AssignmentForm $form, &$errors, $alert=true) {
+
+        $assignee = $form->getAssignee();
+        if ($assignee instanceof Staff) {
+            if ($this->getStaffId() == $assignee->getId()) {
+                $errors['assignee'] = sprintf(__('%s already assigned to %s'),
+                        __('Task'),
+                        __('the agent')
+                        );
+            } elseif(!$assignee->isAvailable()) {
+                $errors['assignee'] = __('Agent is unavailable for assignment');
+            } else {
+                $this->staff_id = $assignee->getId();
+            }
+        } elseif ($assignee instanceof Team) {
+            if ($this->getTeamId() == $assignee->getId()) {
+                $errors['assignee'] = sprintf(__('%s already assigned to %s'),
+                        __('Task'),
+                        __('the team')
+                        );
+            } else {
+                $this->team_id = $assignee->getId();
 
-        if (!isset($vars['staff_id']) || !($staff=Staff::lookup($vars['staff_id'])))
-            $errors['staff_id'] = __('Agent selection required');
-        elseif ($staff->getid() == $this->getStaffId())
-            $errors['dept_id'] = __('Task already assigned to agent');
-        else
-            $this->staff_id = $staff->getId();
+            }
+        } else {
+            $errors['assignee'] = __('Unknown assignee');
+        }
 
-        if ($errors || !$this->save())
+        if ($errors || !$this->save(true))
             return false;
 
-        // Transfer completed... post internal note.
+        $this->onAssignment($assignee,
+                $form->getField('comments')->getClean(),
+                $alert);
+
+        return true;
+    }
+
+    function onAssignment($assignee, $note='', $alert=true) {
+        global $thisstaff;
+
+        if (!is_object($assignee))
+            return false;
+
+        $assigner = $thisstaff ?: __('SYSTEM (Auto Assignment)');
+        //Assignment completed... post internal note.
         $title = sprintf(__('Task assigned to %s'),
-                $staff->getName());
-        if ($vars['comments']) {
-            $note = $vars['comments'];
-        } else {
+                (string) $assignee);
+
+        if (!$note) {
             $note = $title;
             $title = '';
         }
 
-        $this->postNote(
+        $errors = array();
+        $note = $this->postNote(
                 array('note' => $note, 'title' => $title),
                 $errors,
-                $thisstaff);
+                $assigner,
+                false);
+
+        // Send alerts out
+        if (!$alert)
+            return false;
 
         return true;
     }
 
-    function transfer($vars, &$errors) {
+    function transfer(TransferForm $form, &$errors, $alert=true) {
         global $thisstaff;
 
-        if (!isset($vars['dept_id']) || !($dept=Dept::lookup($vars['dept_id'])))
-            $errors['dept_id'] = __('Department selection required');
+        $dept = $form->getDept();
+        if (!$dept || !($dept instanceof Dept))
+            $errors['dept'] = __('Department selection required');
         elseif ($dept->getid() == $this->getDeptId())
-            $errors['dept_id'] = __('Task already in the department');
+            $errors['dept'] = __('Task already in the department');
         else
             $this->dept_id = $dept->getId();
 
@@ -360,19 +497,24 @@ class Task extends TaskModel {
             return false;
 
         // Transfer completed... post internal note.
-        $title = sprintf(__('Task transfered to %s department'),
+        $title = sprintf(__('%s transferred to %s department'),
+                __('Task'),
                 $dept->getName());
-        if ($vars['comments']) {
-            $note = $vars['comments'];
-        } else {
+
+        $note = $form->getField('comments')->getClean();
+        if (!$note) {
             $note = $title;
             $title = '';
         }
-
-        $this->postNote(
+        $_errors = array();
+        $note = $this->postNote(
                 array('note' => $note, 'title' => $title),
-                $errors,
-                $thisstaff);
+                $_errors, $thisstaff, false);
+
+        // Send alerts if requested && enabled.
+        if (!$alert)
+            return true;
+
 
         return true;
     }
@@ -392,14 +534,8 @@ class Task extends TaskModel {
         if (!($note=$this->getThread()->addNote($vars, $errors)))
             return null;
 
-        if (isset($vars['task_status'])) {
-            if ($vars['task_status'])
-                $this->reopen();
-            else
-                $this->close();
-
-            $this->save(true);
-        }
+        if (isset($vars['task_status']))
+            $this->setStatus($vars['task_status']);
 
         return $note;
     }
@@ -496,8 +632,86 @@ class Task extends TaskModel {
             }
         }
     }
+
+    /* Quick staff's stats */
+    static function getStaffStats($staff) {
+        global $cfg;
+
+        /* Unknown or invalid staff */
+        if (!$staff
+                || (!is_object($staff) && !($staff=Staff::lookup($staff)))
+                || !$staff->isStaff())
+            return null;
+
+        $where = array('(task.staff_id='.db_input($staff->getId())
+                    .sprintf(' AND task.flags & %d != 0 ', TaskModel::ISOPEN)
+                    .') ');
+        $where2 = '';
+
+        if(($teams=$staff->getTeams()))
+            $where[] = ' ( flags.team_id IN('.implode(',', db_input(array_filter($teams)))
+                        .') AND '
+                        .sprintf('task.flags & %d != 0 ', TaskModel::ISOPEN)
+                        .')';
+
+        if(!$staff->showAssignedOnly() && ($depts=$staff->getDepts())) //Staff with limited access just see Assigned tickets.
+            $where[] = 'task.dept_id IN('.implode(',', db_input($depts)).') ';
+
+        $where = implode(' OR ', $where);
+        if ($where) $where = 'AND ( '.$where.' ) ';
+
+        $sql =  'SELECT \'open\', count(task.id ) AS tasks '
+                .'FROM ' . TASK_TABLE . ' task '
+                . sprintf(' WHERE task.flags & %d != 0 ', TaskModel::ISOPEN)
+                . $where . $where2
+
+                .'UNION SELECT \'overdue\', count( task.id ) AS tasks '
+                .'FROM ' . TASK_TABLE . ' task '
+                . sprintf(' WHERE task.flags & %d != 0 ', TaskModel::ISOPEN)
+                . sprintf(' AND task.flags & %d != 0 ', TaskModel::ISOVERDUE)
+                . $where
+
+                .'UNION SELECT \'assigned\', count( task.id ) AS tasks '
+                .'FROM ' . TASK_TABLE . ' task '
+                . sprintf(' WHERE task.flags & %d != 0 ', TaskModel::ISOPEN)
+                .'AND task.staff_id = ' . db_input($staff->getId()) . ' '
+                . $where
+
+                .'UNION SELECT \'closed\', count( task.id ) AS tasks '
+                .'FROM ' . TASK_TABLE . ' task '
+                . sprintf(' WHERE task.flags & %d = 0 ', TaskModel::ISOPEN)
+                . $where;
+
+        $res = db_query($sql);
+        $stats = array();
+        while ($row = db_fetch_row($res))
+            $stats[$row[0]] = $row[1];
+
+        return $stats;
+    }
+
+    static function getAgentActions($agent, $options=array()) {
+        if (!$agent)
+            return;
+
+        require STAFFINC_DIR.'templates/tasks-actions.tmpl.php';
+    }
 }
 
+
+class TaskCData extends VerySimpleModel {
+    static $meta = array(
+        'pk' => array('task_id'),
+        'table' => TASK_CDATA_TABLE,
+        'joins' => array(
+            'task' => array(
+                'constraint' => array('task_id' => 'TaskModel.task_id'),
+            ),
+        ),
+    );
+}
+
+
 class TaskForm extends DynamicForm {
     static $instance;
     static $defaultForm;
@@ -505,6 +719,12 @@ class TaskForm extends DynamicForm {
 
     static $forms;
 
+    static $cdata = array(
+            'table' => TASK_CDATA_TABLE,
+            'object_id' => 'task_id',
+            'object_type' => 'A',
+        );
+
     static function objects() {
         $os = parent::objects();
         return $os->filter(array('type'=>ObjectModel::OBJECT_TYPE_TASK));
@@ -533,7 +753,7 @@ class TaskForm extends DynamicForm {
 
     static function getInternalForm($source=null) {
         if (!isset(static::$internalForm))
-            static::$internalForm = new Form(self::getInternalFields(), $source);
+            static::$internalForm = new SimpleForm(self::getInternalFields(), $source);
 
         return static::$internalForm;
     }
diff --git a/include/class.template.php b/include/class.template.php
index a48e5255d47dac70bea9234388b6ef81f35b6bf3..c1bdb80df2135a6b81c09a33c6d7973dbd03c5a4 100644
--- a/include/class.template.php
+++ b/include/class.template.php
@@ -23,12 +23,13 @@ class EmailTemplateGroup {
     var $_templates;
     static $all_groups = array(
         'sys' => /* @trans */ 'System Management Templates',
-        'ticket.user' => /* @trans */ 'End-User Email Templates',
-        'ticket.staff' => /* @trans */ 'Agent Email Templates',
+        'a.ticket.user' => /* @trans */ 'Ticket End-User Email Templates',
+        'b.ticket.staff' => /* @trans */ 'Ticket Agent Email Templates',
+        'c.task' => /* @trans */ 'Task Email Templates',
     );
     static $all_names=array(
         'ticket.autoresp'=>array(
-            'group'=>'ticket.user',
+            'group'=>'a.ticket.user',
             'name'=>/* @trans */ 'New Ticket Auto-response',
             'desc'=>/* @trans */ 'Autoresponse sent to user, if enabled, on new ticket.',
             'context' => array(
@@ -36,7 +37,7 @@ class EmailTemplateGroup {
             ),
         ),
         'ticket.autoreply'=>array(
-            'group'=>'ticket.user',
+            'group'=>'a.ticket.user',
             'name'=>/* @trans */ 'New Ticket Auto-reply',
             'desc'=>/* @trans */ 'Canned Auto-reply sent to user on new ticket, based on filter matches. Overwrites "normal" auto-response.',
             'context' => array(
@@ -44,7 +45,7 @@ class EmailTemplateGroup {
             ),
         ),
         'message.autoresp'=>array(
-            'group'=>'ticket.user',
+            'group'=>'a.ticket.user',
             'name'=>/* @trans */ 'New Message Auto-response',
             'desc'=>/* @trans */ 'Confirmation sent to user when a new message is appended to an existing ticket.',
             'context' => array(
@@ -52,7 +53,7 @@ class EmailTemplateGroup {
             ),
         ),
         'ticket.notice'=>array(
-            'group'=>'ticket.user',
+            'group'=>'a.ticket.user',
             'name'=>/* @trans */ 'New Ticket Notice',
             'desc'=>/* @trans */ 'Notice sent to user, if enabled, on new ticket created by an agent on their behalf (e.g phone calls).',
             'context' => array(
@@ -60,7 +61,7 @@ class EmailTemplateGroup {
             ),
         ),
         'ticket.overlimit'=>array(
-            'group'=>'ticket.user',
+            'group'=>'a.ticket.user',
             'name'=>/* @trans */ 'Over Limit Notice',
             'desc'=>/* @trans */ 'A one-time notice sent, if enabled, when user has reached the maximum allowed open tickets.',
             'context' => array(
@@ -68,7 +69,7 @@ class EmailTemplateGroup {
             ),
         ),
         'ticket.reply'=>array(
-            'group'=>'ticket.user',
+            'group'=>'a.ticket.user',
             'name'=>/* @trans */ 'Response/Reply Template',
             'desc'=>/* @trans */ 'Template used on ticket response/reply',
             'context' => array(
@@ -76,7 +77,7 @@ class EmailTemplateGroup {
             ),
         ),
         'ticket.activity.notice'=>array(
-            'group'=>'ticket.user',
+            'group'=>'a.ticket.user',
             'name'=>/* @trans */ 'New Activity Notice',
             'desc'=>/* @trans */ 'Template used to notify collaborators on ticket activity (e.g CC on reply)',
             'context' => array(
@@ -84,7 +85,7 @@ class EmailTemplateGroup {
             ),
         ),
         'ticket.alert'=>array(
-            'group'=>'ticket.staff',
+            'group'=>'b.ticket.staff',
             'name'=>/* @trans */ 'New Ticket Alert',
             'desc'=>/* @trans */ 'Alert sent to agents, if enabled, on new ticket.',
             'context' => array(
@@ -92,7 +93,7 @@ class EmailTemplateGroup {
             ),
         ),
         'message.alert'=>array(
-            'group'=>'ticket.staff',
+            'group'=>'b.ticket.staff',
             'name'=>/* @trans */ 'New Message Alert',
             'desc'=>/* @trans */ 'Alert sent to agents, if enabled, when user replies to an existing ticket.',
             'context' => array(
@@ -100,7 +101,7 @@ class EmailTemplateGroup {
             ),
         ),
         'note.alert'=>array(
-            'group'=>'ticket.staff',
+            'group'=>'b.ticket.staff',
             'name'=>/* @trans */ 'Internal Activity Alert',
             'desc'=>/* @trans */ 'Alert sent out to Agents when internal activity such as an internal note or an agent reply is appended to a ticket.',
             'context' => array(
@@ -108,7 +109,7 @@ class EmailTemplateGroup {
             ),
         ),
         'assigned.alert'=>array(
-            'group'=>'ticket.staff',
+            'group'=>'b.ticket.staff',
             'name'=>/* @trans */ 'Ticket Assignment Alert',
             'desc'=>/* @trans */ 'Alert sent to agents on ticket assignment.',
             'context' => array(
@@ -116,7 +117,7 @@ class EmailTemplateGroup {
             ),
         ),
         'transfer.alert'=>array(
-            'group'=>'ticket.staff',
+            'group'=>'b.ticket.staff',
             'name'=>/* @trans */ 'Ticket Transfer Alert',
             'desc'=>/* @trans */ 'Alert sent to agents on ticket transfer.',
             'context' => array(
@@ -124,13 +125,45 @@ class EmailTemplateGroup {
             ),
         ),
         'ticket.overdue'=>array(
-            'group'=>'ticket.staff',
+            'group'=>'b.ticket.staff',
             'name'=>/* @trans */ 'Overdue Ticket Alert',
             'desc'=>/* @trans */ 'Alert sent to agents on stale or overdue tickets.',
             'context' => array(
                 'ticket', 'recipient', 'comments',
             ),
         ),
+        'task.alert' => array(
+            'group'=>'c.task',
+            'name'=>/* @trans */ 'New Task Alert',
+            'desc'=>/* @trans */ 'Alert sent to agents, if enabled, on new task.',
+        ),
+        'task.activity.notice' => array(
+            'group'=>'c.task',
+            'name'=>/* @trans */ 'New Activity Notice',
+            'desc'=>/* @trans */ 'Template used to notify collaborators on task activity.'
+        ),
+        'task.activity.alert'=>array(
+            'group'=>'c.task',
+            'name'=>/* @trans */ 'New Activity Alert',
+            'desc'=>/* @trans */ 'Alert sent to selected agents, if enabled, on new activity.',
+
+        ),
+        'task.assignment.alert' => array(
+            'group'=>'c.task',
+            'name'=>/* @trans */ 'Task Assignment Alert',
+            'desc'=>/* @trans */ 'Alert sent to agents on task assignment.',
+        ),
+        'task.transfer.alert'=>array(
+            'group'=>'c.task',
+            'name'=>/* @trans */ 'Task Transfer Alert',
+            'desc'=>/* @trans */ 'Alert sent to agents on task transfer.',
+        ),
+        'task.overdue.alert'=>array(
+            'group'=>'c.task',
+            'name'=>/* @trans */ 'Overdue Task Alert',
+            'desc'=>/* @trans */ 'Alert sent to agents on stale or overdue
+            task.',
+        ),
     );
 
     function EmailTemplateGroup($id){
@@ -300,6 +333,27 @@ class EmailTemplateGroup {
         return $this->getMsgTemplate('ticket.overdue');
     }
 
+    /* Tasks templates */
+    function getNewTaskAlertMsgTemplate() {
+        return $this->getMsgTemplate('task.alert');
+    }
+
+    function  getTaskActivityNoticeMsgTemplate() {
+        return $this->getMsgTemplate('task.activity.notice');
+    }
+
+    function getTaskTransferAlertMsgTemplate() {
+        return $this->getMsgTemplate('task.transfer.alert');
+    }
+
+    function getTaskAssignmentAlertMsgTemplate() {
+        return $this->getMsgTemplate('task.assignment.alert');
+    }
+
+    function getTaskOverdueAlertMsgTemplate() {
+        return $this->getMsgTemplate('task.overdue.alert');
+    }
+
     function update($vars,&$errors) {
         if(!$vars['isactive'] && $this->isInUse())
             $errors['isactive']=__('In-use template set cannot be disabled!');
diff --git a/include/class.thread.php b/include/class.thread.php
index 4b3771e10c864cf0c611ea4d6b35d9a23b333fe9..bd50ecb20c48a8c7e3a2e935cfdf878042c07748 100644
--- a/include/class.thread.php
+++ b/include/class.thread.php
@@ -89,6 +89,15 @@ class Thread extends VerySimpleModel {
         return $base;
     }
 
+    function render($type=false) {
+
+        $entries = $this->getEntries();
+        if ($type && is_array($type))
+            $entries->filter(array('type__in' => $type));
+
+        include STAFFINC_DIR . 'templates/thread-entries.tmpl.php';
+    }
+
     function getEntry($id) {
         return ThreadEntry::lookup($id, $this->getId());
     }
diff --git a/include/class.ticket.php b/include/class.ticket.php
index c275127847b51731ed838b4240d7d56d8bd594e1..cbb51b4143eca41bdfdf9570835237d5e51db4be 100644
--- a/include/class.ticket.php
+++ b/include/class.ticket.php
@@ -876,10 +876,11 @@ implements RestrictedAccess, Threadable, TemplateVariable {
     }
 
     function getThreadEntries($type=false) {
-        $thread = $this->getThread()->getEntries();
+        $entries = $this->getThread()->getEntries();
         if ($type && is_array($type))
-            $thread->filter(array('type__in' => $type));
-        return $thread;
+            $entries->filter(array('type__in' => $type));
+
+        return $entries;
     }
 
     //Collaborators
diff --git a/include/i18n/en_US/help/tips/settings.tasks.yaml b/include/i18n/en_US/help/tips/settings.tasks.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..6d1f056187ec87bc7e9ce8d42a05bd702f5bf7c0
--- /dev/null
+++ b/include/i18n/en_US/help/tips/settings.tasks.yaml
@@ -0,0 +1,110 @@
+#
+# This is popup help messages for the Admin Panel -> Settings -> Tasks
+#
+# Fields:
+# title - Shown in bold at the top of the popover window
+# content - The body of the help popover
+# links - List of links shows below the content
+#   title - Link title
+#   href - href of link (links starting with / are translated to the
+#       helpdesk installation path)
+#
+# The key names such as 'helpdesk_name' should not be translated as they
+# must match the HTML #ids put into the page template.
+#
+---
+number_format:
+    title: Task Number Format
+    content: >
+        This setting is used to generate task numbers. Use hash signs
+        (`#`) where digits are to be placed. Any other text in the number
+        format will be preserved.
+        <br/><br/>
+        For example, for six-digit numbers, use <code>######</code>.
+
+sequence_id:
+    title: Task Number Sequence
+    content: >
+        Choose a sequence from which to derive new task numbers. The
+        system has a incrementing sequence and a random sequence by default.
+        You may create as many sequences as you wish.
+
+default_sla:
+    title: Default SLA
+    content: >
+        Choose the default Service Level Agreement to manage how long a task
+        can remain Open before it is rendered Overdue.
+    links:
+      - title: Create more SLA Plans
+        href: /scp/slas.php
+
+default_priority:
+    title: Default Priority
+    content: >
+        Choose a default <span class="doc-desc-title">priority</span> for
+        tasks not assigned a priority automatically.
+
+task_attachment_settings:
+    title: Task Thread Attachments
+    content: >
+        Configure settings for files attached to the <span
+        class="doc-desc-title">description</span> field. These settings
+        are used for all new tasks and new messages regardless of the
+        source channel (web portal, email, api, etc.).
+
+page_title:
+    title: Alerts and Notices
+    content: >
+        Alerts and Notices are automated email notifications sent to Agents
+        when various task events are triggered.
+
+task_alert:
+    title: New Task Alert
+    content: >
+        <p>
+        Alert sent out to Agents when a new task is created.
+        </p><p class="info-banner">
+        <i class="icon-info-sign"></i>
+        This alert is not sent out if the task is auto-assigned.
+        </p>
+    links:
+      - title: Default New Task Alert Template
+        href: /scp/templates.php?default_for=task.alert
+
+activity_alert:
+    title: New Activity Alert
+    content: >
+        Alert sent out to Agents when a new message is appended to an
+        existing task.
+    links:
+      - title: Default New Activity Alert Template
+        href: /scp/templates.php?default_for=task.activity.alert
+
+assignment_alert:
+    title: Task Assignment Alert
+    content: >
+        Alert sent out to Agents on task assignment.
+    links:
+      - title: Default Task Assignment Alert Template
+        href: /scp/templates.php?default_for=task.assignment.alert
+
+transfer_alert:
+    title: Task Transfer Alert
+    content: >
+        Alert sent out to Agents on task transfer between Departments.
+    links:
+      - title: Default Task Transfer Alert Template
+        href: /scp/templates.php?default_for=task.transfer.alert
+
+overdue_alert:
+    title: Overdue Task Alert
+    content: >
+        Alert sent out to Agents when a task becomes overdue based on SLA
+        or Due Date.
+    links:
+      - title: Default Stale Task Alert Template
+        href: /scp/templates.php?default_for=task.overdue.alert
+
+      - title: Manage SLAs
+        href: /scp/slas.php
+
diff --git a/include/i18n/en_US/templates/email/task.activity.alert.yaml b/include/i18n/en_US/templates/email/task.activity.alert.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..82e7c4ce04474cf4214844d8492560074451dc1d
--- /dev/null
+++ b/include/i18n/en_US/templates/email/task.activity.alert.yaml
@@ -0,0 +1,54 @@
+#
+# Email template: task.activity.alert.yaml
+#
+# Sent to agents when a new note/message is posted to a task.
+# This can occur if a collaborator or an agent responds to an email from the
+# system or visits the web portal and posts a new message there.
+#
+#
+---
+notes: |
+    Sent to agents when a new message/note is posted to a task.  This can
+    occur if a collaborator or an agent responds to an email from the system
+    or visits the web portal and posts a new message there.
+
+subject: |
+    New Activity Alert
+body: |
+    <h3><strong>Hi %{recipient.name},</strong></h3>
+    New message appended to task <a
+    href="%{ticket.staff_link}">#%{task.number}</a>
+    <br>
+    <br>
+    <table>
+    <tbody>
+    <tr>
+        <td>
+             <strong>Title</strong>:
+        </td>
+        <td>
+             %{task.title}
+        </td>
+    </tr>
+    <tr>
+        <td>
+             <strong>Department</strong>:
+        </td>
+        <td>
+             %{task.dept.name}
+        </td>
+    </tr>
+    </tbody>
+    </table>
+    <br>
+    %{message}
+    <br>
+    <br>
+    <hr>
+    <div>To view or respond to the task, please <a
+    href="%{task.staff_link}"><span style="color: rgb(84, 141, 212);"
+    >login</span></a> to the support system</div>
+    <em style="color: rgb(127,127,127); font-size: small; ">Your friendly
+    Customer Support System</em><br>
+    <img src="cid:b56944cb4722cc5cda9d1e23a3ea7fbc"
+    alt="Powered by osTicket" width="126" height="19" style="width: 126px;">
diff --git a/include/i18n/en_US/templates/email/task.activity.notice.yaml b/include/i18n/en_US/templates/email/task.activity.notice.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..45d1e3e51aecdf64851b0f54fc230f71824be969
--- /dev/null
+++ b/include/i18n/en_US/templates/email/task.activity.notice.yaml
@@ -0,0 +1,28 @@
+#
+# Email template: task.activity.notice.yaml
+#
+# Notice sent to collaborators on task activity e.g reply or message
+#
+---
+notes: |
+    Notice sent to collaborators on task activity e.g note or message.
+
+subject: |
+    Re: %{task.title} [#%{task.number}]
+body: |
+    <h3><strong>Dear %{recipient.name.first},</strong></h3>
+    <div>
+        <em>%{poster.name}</em> just logged a message to a task in which you participate.
+    </div>
+    <br>
+    %{message}
+    <br>
+    <br>
+    <hr>
+    <div style="color: rgb(127, 127, 127); font-size: small; text-align: center;">
+    <em>You're getting this email because you are a collaborator
+    on task <a href="%{recipient.task_link}" style="color: rgb(84, 141, 212);"
+    >#%{task.number}</a>.  To participate, simply reply to this email
+    or <a href="%{recipient.task_link}" style="color: rgb(84, 141, 212);"
+    >click here</a> for a complete archive of the task thread.</em>
+    </div>
diff --git a/include/i18n/en_US/templates/email/task.alert.yaml b/include/i18n/en_US/templates/email/task.alert.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..08e311fe09f322b8c5606fb1d4e28d8d614aa678
--- /dev/null
+++ b/include/i18n/en_US/templates/email/task.alert.yaml
@@ -0,0 +1,41 @@
+#
+# Email template: task.alert.yaml
+#
+# Sent to an agent when a new task is created in the system.
+#
+#
+---
+notes: |
+    Sent to an agent when a new task is created in the system.
+
+subject: |
+    New Task Alert
+body: |
+    <h2>Hi %{recipient.name},</h2>
+    New task #%{task.number} created
+    <br>
+    <br>
+    <table>
+    <tbody>
+    <tr>
+        <td>
+            <strong>Department</strong>:
+        </td>
+        <td>
+            %{task.dept.name}
+        </td>
+    </tr>
+    </tbody>
+    </table>
+    <br>
+    %{task.description}
+    <br>
+    <br>
+    <hr>
+    <div>To view or respond to the ticket, please <a
+    href="%{task.staff_link}">login</a> to the support system</div>
+    <em style="font-size: small">Your friendly Customer Support System</em>
+    <br>
+    <a href="http://osticket.com/"><img width="126" height="19"
+        style="width: 126px; " alt="Powered By osTicket"
+        src="cid:b56944cb4722cc5cda9d1e23a3ea7fbc"/></a>
diff --git a/include/i18n/en_US/templates/email/task.assignment.alert.yaml b/include/i18n/en_US/templates/email/task.assignment.alert.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..4c509b08bb812f629966688d727e09a494256ac9
--- /dev/null
+++ b/include/i18n/en_US/templates/email/task.assignment.alert.yaml
@@ -0,0 +1,45 @@
+#
+# Email template: task.assignment.alert.yaml
+#
+# Sent to agents when a ticket is assigned to them or the team to which
+# they belong.
+# Use %{assigner} to distinguish who made the assignment.
+#
+---
+notes: |
+    Sent to agents when a ticket is assigned to them or the team to which
+    they belong. Use %{assigner} to distinguish who made the assignment.
+
+subject: |
+    Task Assigned to you
+body: |
+    <h3><strong>Hi %{assignee.name.first},</strong></h3>
+    Task <a href="%{task.staff_link}">#%{task.number}</a> has been
+    assigned to you by %{assigner.name.short}
+    <br>
+    <br>
+    <table>
+    <tbody>
+    <tr>
+        <td>
+             <strong>Title</strong>:
+        </td>
+        <td>
+             %{task.title}
+        </td>
+    </tr>
+    </tbody>
+    </table>
+    <br>
+    %{comments}
+    <br>
+    <br>
+    <hr>
+    <div>To view/respond to the task, please <a
+    href="%{task.staff_link}"><span style="color: rgb(84, 141, 212);"
+    >login</span></a> to the support system</div>
+    <em style="font-size: small; ">Your friendly Customer Support
+    System</em>
+    <br>
+    <img src="cid:b56944cb4722cc5cda9d1e23a3ea7fbc"
+    alt="Powered by osTicket" width="126" height="19" style="width: 126px;">
diff --git a/include/i18n/en_US/templates/email/task.overdue.alert.yaml b/include/i18n/en_US/templates/email/task.overdue.alert.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..0b48a7337e04efd6dbdf6c0f8c7cfeb15d7b463f
--- /dev/null
+++ b/include/i18n/en_US/templates/email/task.overdue.alert.yaml
@@ -0,0 +1,38 @@
+#
+# Email template: task.overdue.alert.yaml
+#
+# Sent to agents when a tasks transitions to overdue in the system.
+# Overdue tasks occur based on set due-date as well as the SLA
+# defined for the task.
+#
+---
+notes: |
+    Sent to agents when a task transitions to overdue in the system.
+    Overdue tasks occur based on the set due-date as well as the SLA
+    defined for the task.
+
+subject: |
+    Stale Task Alert
+body: |
+    <h3><strong>Hi %{recipient.name}</strong>,</h3>
+    A task, <a href="%{task.staff_link}">#%{task.number}</a> is
+    seriously overdue.
+    <br>
+    <br>
+    We should all work hard to guarantee that all tasks are being
+    addressed in a timely manner.
+    <br>
+    <br>
+    Signed,<br>
+    %{task.dept.manager.name}
+    <hr>
+    <div>To view or respond to the task, please <a
+    href="%{task.staff_link}"><span style="color: rgb(84, 141, 212);"
+    >login</span></a> to the support system. You're receiving this
+    notice because the task is assigned directly to you or to a team or
+    department of which you're a member.</div>
+    <em style="font-size: small">Your friendly <span style="font-size: smaller"
+    >(although with limited patience)</span> Customer Support
+    System</em><br>
+    <img src="cid:b56944cb4722cc5cda9d1e23a3ea7fbc" height="19"
+        alt="Powered by osTicket" width="126" style="width: 126px;">
diff --git a/include/i18n/en_US/templates/email/task.transfer.alert.yaml b/include/i18n/en_US/templates/email/task.transfer.alert.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..6e2d26a26865a03c58495045965d87d24e215137
--- /dev/null
+++ b/include/i18n/en_US/templates/email/task.transfer.alert.yaml
@@ -0,0 +1,31 @@
+#
+# Email template: task.transfer.alert.yaml
+#
+# Sent to agents when a task is transfered to their department.
+#
+---
+notes: |
+    Sent to agents when a task is transfered to a department to which
+    they are a member.
+subject: |
+    Task #%{task.number} transfer - %{task.dept.name}
+body: |
+    <h3>Hi %{recipient.name},</h3>
+    Task <a href="%{task.staff_link}">#%{task.number}</a> has been
+    transferred to the %{task.dept.name} department by
+    <strong>%{staff.name.short}</strong>
+    <br>
+    <br>
+    <blockquote>
+        %{comments}
+    </blockquote>
+    <hr>
+    <div>To view or respond to the task, please <a
+    href="%{task.staff_link}">login</a> to the support system.
+    </div>
+    <em style="font-size: small; ">Your friendly Customer Support
+    System</em>
+    <br>
+    <a href="http://osticket.com/"><img width="126" height="19"
+        alt="Powered By osTicket" style="width: 126px;"
+        src="cid:b56944cb4722cc5cda9d1e23a3ea7fbc"/></a>
diff --git a/include/staff/settings-tasks.inc.php b/include/staff/settings-tasks.inc.php
new file mode 100644
index 0000000000000000000000000000000000000000..d6a0d61d69d8cf73028dd566ac74f193486ef98f
--- /dev/null
+++ b/include/staff/settings-tasks.inc.php
@@ -0,0 +1,311 @@
+<?php
+if(!defined('OSTADMININC') || !$thisstaff || !$thisstaff->isAdmin() || !$config) die('Access Denied');
+if(!($maxfileuploads=ini_get('max_file_uploads')))
+    $maxfileuploads=DEFAULT_MAX_FILE_UPLOADS;
+?>
+<h2><?php echo __('Tasks Settings and Options');?></h2>
+<form action="settings.php?t=tasks" method="post" id="save">
+<?php csrf_token(); ?>
+<input type="hidden" name="t" value="tasks" >
+
+<ul class="tabs" id="tasks-tabs">
+    <li class="active"><a href="#settings">
+        <i class="icon-asterisk"></i> <?php echo __('Settings'); ?></a></li>
+    <li><a href="#alerts">
+        <i class="icon-bell-alt"></i> <?php echo __('Alerts &amp; Notices'); ?></a></li>
+</ul>
+<div id="tasks-tabs_container">
+   <div id="settings" class="tab_content">
+    <table class="form_table settings_table" width="940" border="0" cellspacing="0" cellpadding="2">
+        <thead>
+            <tr>
+                <th colspan="2">
+                    <em><?php echo __('Global default task settings and options.'); ?></em>
+                </th>
+            </tr>
+        </thead>
+        <tbody>
+            <tr>
+                <td>
+                    <?php echo __('Default Task Number Format'); ?>:
+                </td>
+                <td>
+                    <input type="text" name="task_number_format" value="<?php
+                    echo $config['task_number_format']; ?>"/>
+                    <span class="faded"><?php echo __('e.g.'); ?> <span id="format-example"><?php
+                        if ($config['task_sequence_id'])
+                            $seq = Sequence::lookup($config['task_sequence_id']);
+                        if (!isset($seq))
+                            $seq = new RandomSequence();
+                        echo $seq->current($config['task_number_format']);
+                        ?></span></span>
+                    <i class="help-tip icon-question-sign" href="#number_format"></i>
+                    <div class="error"><?php echo $errors['task_number_format']; ?></div>
+                </td>
+            </tr>
+            <tr><td width="220"><?php echo __('Default Task Number Sequence'); ?>:</td>
+    <?php $selected = 'selected="selected"'; ?>
+                <td>
+                    <select name="task_sequence_id">
+                    <option value="0" <?php if ($config['task_sequence_id'] == 0) echo $selected;
+                        ?>>&mdash; <?php echo __('Random'); ?> &mdash;</option>
+    <?php foreach (Sequence::objects() as $s) { ?>
+                    <option value="<?php echo $s->id; ?>" <?php
+                        if ($config['task_sequence_id'] == $s->id) echo $selected;
+                        ?>><?php echo $s->name; ?></option>
+    <?php } ?>
+                    </select>
+                    <button class="action-button pull-right" onclick="javascript:
+                    $.dialog('ajax.php/sequence/manage', 205);
+                    return false;
+                    "><i class="icon-gear"></i> <?php echo __('Manage'); ?></button>
+                    <i class="help-tip icon-question-sign" href="#sequence_id"></i>
+                </td>
+            </tr>
+            <tr>
+                <td width="180" class="required"><?php echo __('Default Priority');?>:</td>
+                <td>
+                    <select name="default_task_priority_id">
+                        <?php
+                        $priorities= db_query('SELECT priority_id,priority_desc FROM '.TICKET_PRIORITY_TABLE);
+                        while (list($id,$tag) = db_fetch_row($priorities)){ ?>
+                            <option value="<?php echo $id; ?>"<?php echo
+                                ($config['default_task_priority_id']==$id)?'selected':''; ?>><?php echo $tag; ?></option>
+                        <?php
+                        } ?>
+                    </select>
+                    &nbsp;<span class="error">*&nbsp;<?php echo
+                    $errors['default_task_priority_id']; ?></span> <i class="help-tip icon-question-sign" href="#default_priority"></i>
+                 </td>
+            </tr>
+            <tr>
+                <td width="180" class="required">
+                    <?php echo __('Default SLA');?>:
+                </td>
+                <td>
+                    <span>
+                    <select name="default_task_sla_id">
+                        <option value="0">&mdash; <?php echo __('None');?> &mdash;</option>
+                        <?php
+                        if($slas=SLA::getSLAs()) {
+                            foreach($slas as $id => $name) {
+                                echo sprintf('<option value="%d" %s>%s</option>',
+                                        $id,
+                                        ($config['default_task_sla_id'] &&
+                                         $id==$config['default_task_sla_id'])?'selected="selected"':'',
+                                        $name);
+                            }
+                        }
+                        ?>
+                    </select>
+                    &nbsp;<span class="error">*&nbsp;<?php
+                        echo $errors['default_task_sla_id']; ?></span>  <i class="help-tip icon-question-sign" href="#default_sla"></i>
+                    </span>
+                </td>
+            </tr>
+        </tbody>
+    </table>
+   </div>
+   <div id="alerts" class="tab_content" style="display:none;">
+    <table class="form_table settings_table" width="940" border="0" cellspacing="0" cellpadding="2">
+        <tbody>
+            <tr><th><em><b><?php echo __('New Task Alert'); ?></b>:
+                <i class="help-tip icon-question-sign" href="#task_alert"></i>
+                </em></th></tr>
+            <tr>
+                <td><em><b><?php echo __('Status'); ?>:</b></em> &nbsp;
+                    <input type="radio" name="task_alert_active"  value="1"
+                    <?php echo $config['task_alert_active'] ? 'checked="checked"' : ''; ?>
+                    /> <?php echo __('Enable'); ?>
+                    <input type="radio" name="task_alert_active"  value="0"
+                    <?php echo !$config['task_alert_active'] ? 'checked="checked"' : ''; ?> />
+                    <?php echo __('Disable'); ?>
+                    &nbsp;&nbsp;<font class="error">&nbsp;<?php echo $errors['task_alert_active']; ?></font></em>
+                 </td>
+            </tr>
+            <tr>
+                <td>
+                    <input type="checkbox" name="task_alert_admin" <?php
+                        echo $config['task_alert_admin'] ? 'checked="checked"' : ''; ?>>
+                    <?php echo __('Admin Email'); ?> <em>(<?php echo $cfg->getAdminEmail(); ?>)</em>
+                </td>
+            </tr>
+            <tr>
+                <td>
+                    <input type="checkbox" name="task_alert_dept_manager"
+                    <?php echo $config['task_alert_dept_manager'] ? 'checked="checked"' : ''; ?>>
+                    <?php echo __('Department Manager'); ?>
+                </td>
+            </tr>
+            <tr>
+                <td>
+                    <input type="checkbox" name="task_alert_dept_members"
+                    <?php echo $config['task_alert_dept_members'] ? 'checked="checked"' : ''; ?>>
+                    <?php echo __('Department Members'); ?>
+                </td>
+            </tr>
+            <tr><th><em><b><?php echo __('New Activity Alert'); ?></b>:
+                <i class="help-tip icon-question-sign" href="#activity_alert"></i>
+                </em></th></tr>
+            <tr>
+                <td><em><b><?php echo __('Status'); ?>:</b></em> &nbsp;
+                  <input type="radio" name="task_activity_alert_active" value="1"
+                  <?php echo $config['task_activity_alert_active'] ? 'checked="checked"' : ''; ?> />
+                    <?php echo __('Enable'); ?>
+                  &nbsp;&nbsp;
+                  <input type="radio" name="task_activity_alert_active"  value="0"
+                  <?php echo !$config['task_activity_alert_active'] ? 'checked="checked"' : ''; ?> />
+                    <?php echo __('Disable'); ?>
+                  &nbsp;&nbsp;&nbsp;<font class="error">&nbsp;<?php echo $errors['task_activity_alert_active']; ?></font>
+                </td>
+            </tr>
+            <tr>
+                <td>
+                  <input type="checkbox" name="task_activity_alert_laststaff" <?php
+                  echo $config['task_activity_alert_laststaff'] ? 'checked="checked"' : ''; ?>>
+                  <?php echo __('Last Respondent'); ?>
+                </td>
+            </tr>
+            <tr>
+                <td>
+                  <input type="checkbox" name="task_activity_alert_assigned"
+                  <?php echo $config['task_activity_alert_assigned'] ? 'checked="checked"' : ''; ?>>
+                  <?php echo __('Assigned Agent / Team'); ?>
+                </td>
+            </tr>
+            <tr>
+                <td>
+                  <input type="checkbox" name="task_activity_alert_dept_manager"
+                  <?php echo $config['task_activity_alert_dept_manager'] ? 'checked="checked"' : ''; ?>>
+                    <?php echo __('Department Manager'); ?>
+                </td>
+            </tr>
+            <tr><th><em><b><?php echo __('Task Assignment Alert'); ?></b>:
+                <i class="help-tip icon-question-sign" href="#assignment_alert"></i>
+                </em></th></tr>
+            <tr>
+                <td><em><b><?php echo __('Status'); ?>: </b></em> &nbsp;
+                  <input name="task_assignment_alert_active" value="1" type="radio"
+                    <?php echo $config['task_assignment_alert_active'] ? 'checked="checked"' : ''; ?>>
+                    <?php echo __('Enable'); ?>
+                    &nbsp;&nbsp;
+                  <input name="task_assignment_alert_active" value="0" type="radio"
+                    <?php echo !$config['task_assignment_alert_active'] ? 'checked="checked"' : ''; ?>>
+                    <?php echo __('Disable'); ?>
+                   &nbsp;&nbsp;&nbsp;<font class="error">&nbsp;<?php echo $errors['task_assignment_alert_active']; ?></font>
+                </td>
+            </tr>
+            <tr>
+                <td>
+                  <input type="checkbox" name="task_assignment_alert_staff" <?php echo
+                  $config['task_assignment_alert_staff'] ? 'checked="checked"' : ''; ?>>
+                  <?php echo __('Assigned Agent / Team'); ?>
+                </td>
+            </tr>
+            <tr>
+                <td>
+                  <input type="checkbox"name="task_assignment_alert_team_lead" <?php
+                  echo $config['task_assignment_alert_team_lead'] ? 'checked="checked"' : ''; ?>>
+                  <?php echo __('Team Lead'); ?>
+                </td>
+            </tr>
+            <tr>
+                <td>
+                  <input type="checkbox"name="task_assignment_alert_team_members"
+                  <?php echo $config['task_assignment_alert_team_members'] ? 'checked="checked"' : ''; ?>>
+                    <?php echo __('Team Members'); ?>
+                </td>
+            </tr>
+            <tr><th><em><b><?php echo __('Task Transfer Alert'); ?></b>:
+                <i class="help-tip icon-question-sign" href="#transfer_alert"></i>
+                </em></th></tr>
+            <tr>
+                <td><em><b><?php echo __('Status'); ?>:</b></em> &nbsp;
+                <input type="radio" name="task_transfer_alert_active"  value="1"
+                <?php echo $config['task_transfer_alert_active'] ? 'checked="checked"' : ''; ?> />
+                    <?php echo __('Enable'); ?>
+                <input type="radio" name="task_transfer_alert_active"  value="0"
+                <?php echo !$config['task_transfer_alert_active'] ? 'checked="checked"' : ''; ?> />
+                    <?php echo __('Disable'); ?>
+                  &nbsp;&nbsp;&nbsp;<font class="error">&nbsp;<?php
+                  echo $errors['task_transfer_alert_active']; ?></font>
+                </td>
+            </tr>
+            <tr>
+                <td>
+                  <input type="checkbox" name="task_transfer_alert_assigned"
+                  <?php echo $config['task_transfer_alert_assigned']?'checked="checked"':''; ?>>
+                    <?php echo __('Assigned Agent / Team'); ?>
+                </td>
+            </tr>
+            <tr>
+                <td>
+                  <input type="checkbox" name="task_transfer_alert_dept_manager"
+                  <?php echo $config['task_transfer_alert_dept_manager'] ? 'checked="checked"' : ''; ?>>
+                    <?php echo __('Department Manager'); ?>
+                </td>
+            </tr>
+            <tr>
+                <td>
+                  <input type="checkbox" name="task_transfer_alert_dept_members"
+                  <?php echo $config['task_transfer_alert_dept_members'] ? 'checked="checked"' : ''; ?>>
+                    <?php echo __('Department Members'); ?>
+                </td>
+            </tr>
+            <tr><th><em><b><?php echo __('Overdue Task Alert'); ?></b>:
+                <i class="help-tip icon-question-sign" href="#overdue_alert"></i>
+                </em></th></tr>
+            <tr>
+                <td><em><b><?php echo __('Status'); ?>:</b></em> &nbsp;
+                  <input type="radio" name="task_overdue_alert_active"  value="1"
+                    <?php echo $config['task_overdue_alert_active'] ? 'checked="checked"' : ''; ?> /> <?php echo __('Enable'); ?>
+                  <input type="radio" name="task_overdue_alert_active"  value="0"
+                    <?php echo !$config['task_overdue_alert_active'] ? 'checked="checked"' : ''; ?> /> <?php echo __('Disable'); ?>
+                  &nbsp;&nbsp;<font class="error">&nbsp;<?php echo $errors['task_overdue_alert_active']; ?></font>
+                </td>
+            </tr>
+            <tr>
+                <td>
+                  <input type="checkbox" name="task_overdue_alert_assigned" <?php
+                    echo $config['task_overdue_alert_assigned'] ? 'checked="checked"' : ''; ?>>
+                    <?php echo __('Assigned Agent / Team'); ?>
+                </td>
+            </tr>
+            <tr>
+                <td>
+                  <input type="checkbox" name="task_overdue_alert_dept_manager" <?php
+                    echo $config['task_overdue_alert_dept_manager'] ? 'checked="checked"' : ''; ?>>
+                    <?php echo __('Department Manager'); ?>
+                </td>
+            </tr>
+            <tr>
+                <td>
+                  <input type="checkbox" name="task_overdue_alert_dept_members" <?php
+                    echo $config['task_overdue_alert_dept_members'] ? 'checked="checked"' : ''; ?>>
+                    <?php echo __('Department Members'); ?>
+                </td>
+            </tr>
+        </tbody>
+    </table>
+   </div>
+</div>
+<p style="padding-left:250px;">
+    <input class="button" type="submit" name="submit" value="<?php echo __('Save Changes');?>">
+    <input class="button" type="reset" name="reset" value="<?php echo __('Reset Changes');?>">
+</p>
+</form>
+<script type="text/javascript">
+$(function() {
+    var request = null,
+      update_example = function() {
+      request && request.abort();
+      request = $.get('ajax.php/sequence/'
+        + $('[name=task_sequence_id] :selected').val(),
+        {'format': $('[name=task_number_format]').val()},
+        function(data) { $('#format-example').text(data); }
+      );
+    };
+    $('[name=task_sequence_id]').on('change', update_example);
+    $('[name=task_number_format]').on('keyup', update_example);
+});
+</script>
diff --git a/include/staff/task-view.inc.php b/include/staff/task-view.inc.php
new file mode 100644
index 0000000000000000000000000000000000000000..a6b140481ad83effa08d99218803327a19c814e4
--- /dev/null
+++ b/include/staff/task-view.inc.php
@@ -0,0 +1,5 @@
+<div id="task_content">
+<?php
+require STAFFINC_DIR.'templates/task-view.tmpl.php';
+?>
+</div>
diff --git a/include/staff/tasks.inc.php b/include/staff/tasks.inc.php
new file mode 100644
index 0000000000000000000000000000000000000000..cd07dfa21bbd3edb44012f34cba80a06ecc9dd37
--- /dev/null
+++ b/include/staff/tasks.inc.php
@@ -0,0 +1,402 @@
+<?php
+$tasks = TaskModel::objects();
+$date_header = $date_col = false;
+
+// Figure out REFRESH url — which might not be accurate after posting a
+// response
+list($path,) = explode('?', $_SERVER['REQUEST_URI'], 2);
+$args = array();
+parse_str($_SERVER['QUERY_STRING'], $args);
+
+// Remove commands from query
+unset($args['id']);
+unset($args['a']);
+
+$refresh_url = $path . '?' . http_build_query($args);
+
+$queue_name = strtolower($_GET['status'] ?: $_GET['a']); //Status is overloaded
+switch ($queue_name) {
+case 'closed':
+    $status='closed';
+    $results_type=__('Closed Tasks');
+    $showassigned=true; //closed by.
+    break;
+case 'overdue':
+    $status='open';
+    $results_type=__('Overdue Tasks');
+    $tasks->filter(array('isoverdue'=>1));
+    break;
+case 'assigned':
+    $status='open';
+    $staffId=$thisstaff->getId();
+    $results_type=__('My Tasks');
+    $tasks->filter(array('staff_id'=>$thisstaff->getId()));
+    break;
+default:
+case 'search':
+    // Consider basic search
+    if ($_REQUEST['query']) {
+        $results_type=__('Search Results');
+        $tasks = $tasks->filter(Q::any(array(
+            'number__startswith' => $_REQUEST['query'],
+            'cdata__title__contains' => $_REQUEST['query'],
+        )));
+        break;
+    } elseif (isset($_SESSION['advsearch'])) {
+        // XXX: De-duplicate and simplify this code
+        $form = $search->getFormFromSession('advsearch');
+        $form->loadState($_SESSION['advsearch']);
+        $tasks = $search->mangleQuerySet($tasks, $form);
+        $results_type=__('Advanced Search')
+            . '<a class="action-button" href="?clear_filter"><i class="icon-ban-circle"></i> <em>' . __('clear') . '</em></a>';
+        break;
+    }
+    // Fall-through and show open tickets
+case 'open':
+    $status='open';
+    $results_type=__('Open Tasks');
+    break;
+}
+
+// Apply filters
+$filters = array();
+$SQ = new Q(array('flags__hasbit' => TaskModel::ISOPEN));
+if ($status && !strcasecmp($status, 'closed'))
+    $SQ->negate();
+
+$filters[] = $SQ;
+$tasks->filter($filters);
+
+// Impose visibility constraints
+// ------------------------------------------------------------
+// -- Open and assigned to me
+$visibility = array(
+    new Q(array('flags__hasbit' => TaskModel::ISOPEN, 'staff_id' => $thisstaff->getId()))
+);
+// -- Routed to a department of mine
+if (!$thisstaff->showAssignedOnly() && ($depts=$thisstaff->getDepts()))
+    $visibility[] = new Q(array('dept_id__in' => $depts));
+// -- Open and assigned to a team of mine
+if (($teams = $thisstaff->getTeams()) && count(array_filter($teams)))
+    $visibility[] = new Q(array(
+        'team_id__in' => array_filter($teams),
+        'flags__hasbit' => TaskModel::ISOPEN
+    ));
+$tasks->filter(Q::any($visibility));
+
+// Add in annotations
+$tasks->annotate(array(
+    //'collab_count' => SqlAggregate::COUNT('collaborators'),
+    'attachment_count' => SqlAggregate::COUNT('thread__entries__attachments'),
+    'thread_count' => SqlAggregate::COUNT('thread__entries'),
+    // 'isopen' => new SqlExpr(array('flags__hasbit' => TaskModel::ISOPEN)),
+));
+
+$tasks->values('id', 'number', 'created', 'staff_id', 'team_id',
+        'staff__firstname', 'staff__lastname', 'team__name',
+        'dept__name', 'cdata__title', 'flags');
+// Apply requested quick filter
+
+// Apply requested sorting
+$queue_sort_key = sprintf(':Q:%s:sort', $queue_name);
+
+if (isset($_GET['sort']))
+    $_SESSION[$queue_sort_key] = $_GET['sort'];
+switch ($_SESSION[$queue_sort_key]) {
+case 'number':
+    $tasks->extra(array(
+        'order_by'=>array(SqlExpression::times(new SqlField('number'), 1))
+    ));
+    break;
+case 'priority,due':
+    $tasks->order_by('cdata__:priority__priority_urgency');
+    // Fall through to add in due date filter
+case 'due':
+    $date_header = __('Due Date');
+    $date_col = 'est_duedate';
+    $tasks->values('est_duedate');
+    $tasks->filter(array('est_duedate__isnull'=>false));
+    $tasks->order_by('est_duedate');
+    break;
+case 'updated':
+    $tasks->order_by('cdata__:priority__priority_urgency', '-lastupdate');
+    break;
+case 'created':
+default:
+    $tasks->order_by('-created');
+    break;
+}
+
+// Apply requested pagination
+$page=($_GET['p'] && is_numeric($_GET['p']))?$_GET['p']:1;
+$pageNav=new Pagenate($tasks->count(), $page, PAGE_LIMIT);
+$pageNav->setURL('tasks.php', $args);
+$tasks = $pageNav->paginate($tasks);
+
+TaskForm::ensureDynamicDataView();
+
+// Save the query to the session for exporting
+$_SESSION[':Q:tasks'] = $tasks;
+
+// Mass actions
+$actions = array();
+
+if ($thisstaff->hasPerm(Task::PERM_ASSIGN)) {
+    $actions += array(
+            'assign' => array(
+                'icon' => 'icon-user',
+                'action' => __('Assign Tasks')
+            ));
+}
+
+if ($thisstaff->hasPerm(Task::PERM_TRANSFER)) {
+    $actions += array(
+            'transfer' => array(
+                'icon' => 'icon-share',
+                'action' => __('Transfer Tasks')
+            ));
+}
+
+if ($thisstaff->hasPerm(Task::PERM_DELETE)) {
+    $actions += array(
+            'delete' => array(
+                'icon' => 'icon-trash',
+                'action' => __('Delete Tasks')
+            ));
+}
+
+
+?>
+<!-- SEARCH FORM START -->
+<div id='basic_search'>
+    <form action="tasks.php" method="get">
+    <input type="hidden" name="a" value="search">
+    <table>
+        <tr>
+            <td><input type="text" id="basic-ticket-search" name="query"
+            size=30 value="<?php echo Format::htmlchars($_REQUEST['query'],
+            true); ?>"
+                autocomplete="off" autocorrect="off" autocapitalize="off"></td>
+            <td><input type="submit" class="button" value="<?php echo __('Search'); ?>"></td>
+            <td>&nbsp;&nbsp;<a href="#" onclick="javascript:
+                $.dialog('ajax.php/tasks/search', 201);"
+                >[<?php echo __('advanced'); ?>]</a>&nbsp;<i class="help-tip icon-question-sign" href="#advanced"></i></td>
+        </tr>
+    </table>
+    </form>
+</div>
+<!-- SEARCH FORM END -->
+<div class="clear"></div>
+<div style="margin-bottom:20px; padding-top:10px;">
+<div>
+        <div class="pull-left flush-left">
+            <h2><a href="<?php echo $refresh_url; ?>"
+                title="<?php echo __('Refresh'); ?>"><i class="icon-refresh"></i> <?php echo
+                $results_type.$showing; ?></a></h2>
+        </div>
+        <div class="pull-right flush-right">
+            <span style="display:inline-block">
+                <span style="vertical-align: baseline">Sort:</span>
+            <select name="sort" onchange="javascript:addSearchParam('sort', $(this).val());">
+<?php foreach (array(
+    'created' =>    __('Most Recently Created'),
+    'updated' =>    __('Most Recently Updated'),
+    'due' =>        __('Due Soon'),
+    'priority,due' => __('Priority + Due Soon'),
+    'number' =>     __('Task Number'),
+) as $mode => $desc) { ?>
+            <option value="<?php echo $mode; ?>" <?php if ($mode == $_SESSION[$queue_sort_key]) echo 'selected="selected"'; ?>><?php echo $desc; ?></option>
+<?php } ?>
+            </select>
+            </span>
+           <?php
+            Task::getAgentActions($thisstaff, array('status' => $status));
+            ?>
+        </div>
+</div>
+<div class="clear" style="margin-bottom:10px;"></div>
+<form action="tasks.php" method="POST" name='tasks' id="tasks">
+<?php csrf_token(); ?>
+ <input type="hidden" name="a" value="mass_process" >
+ <input type="hidden" name="do" id="action" value="" >
+ <input type="hidden" name="status" value="<?php echo
+ Format::htmlchars($_REQUEST['status'], true); ?>" >
+ <table class="list" border="0" cellspacing="1" cellpadding="2" width="940">
+    <thead>
+        <tr>
+            <?php if ($thisstaff->canManageTickets()) { ?>
+	        <th width="8px">&nbsp;</th>
+            <?php } ?>
+	        <th width="70">
+                <?php echo __('Number'); ?></th>
+	        <th width="70">
+                <?php echo $date_header ?: __('Date'); ?></th>
+	        <th width="280">
+                <?php echo __('Title'); ?></th>
+            <th width="250">
+                <?php echo __('Department');?></th>
+            <th width="250">
+                <?php echo __('Assignee');?></th>
+        </tr>
+     </thead>
+     <tbody>
+        <?php
+        // Setup Subject field for display
+        $total=0;
+        $title_field = TaskForm::getInstance()->getField('title');
+        $ids=($errors && $_POST['tids'] && is_array($_POST['tids']))?$_POST['tids']:null;
+        foreach ($tasks as $T) {
+            $T['isopen'] = ($T['flags'] & TaskModel::ISOPEN != 0); //XXX:
+            $total += 1;
+            $tag=$T['staff_id']?'assigned':'openticket';
+            $flag=null;
+            if($T['lock__staff_id'] && $T['lock__staff_id'] != $thisstaff->getId())
+                $flag='locked';
+            elseif($T['isoverdue'])
+                $flag='overdue';
+
+            $assignee = '';
+            $dept = Dept::getLocalById($T['dept_id'], 'name', $T['dept__name']);
+            $assinee ='';
+            if ($T['staff_id']) {
+                $staff =  new PersonsName($T['staff__firstname'].' '.$T['staff__lastname']);
+                $assignee = sprintf('<span class="Icon staffAssigned">%s</span>',
+                        Format::truncate((string) $staff, 40));
+            } elseif($T['team_id']) {
+                $assignee = sprintf('<span class="Icon teamAssigned">%s</span>',
+                    Format::truncate(Team::getLocalById($T['team_id'], 'name', $T['team__name']),40));
+            }
+
+            $threadcount=$T['thread_count'];
+            $number = $T['number'];
+            if ($T['isopen'])
+                $number = sprintf('<b>%s</b>', $number);
+
+            $title = Format::truncate($title_field->display($title_field->to_php($T['cdata__title'])), 40);
+            ?>
+            <tr id="<?php echo $T['id']; ?>">
+                <?php
+                if ($thisstaff->canManageTickets()) {
+                    $sel = false;
+                    if ($ids && in_array($T['id'], $ids))
+                        $sel = true;
+                    ?>
+                <td align="center" class="nohover">
+                    <input class="ckb" type="checkbox" name="tids[]"
+                        value="<?php echo $T['id']; ?>" <?php echo $sel?'checked="checked"':''; ?>>
+                </td>
+                <?php } ?>
+                <td nowrap>
+                  <a class="preview"
+                    href="tasks.php?id=<?php echo $T['id']; ?>"
+                    data-preview="#tasks/<?php echo $T['id']; ?>/preview"
+                    ><?php echo $number; ?></a></td>
+                <td align="center" nowrap><?php echo
+                Format::datetime($T[$date_col ?: 'created']); ?></td>
+                <td><a <?php if ($flag) { ?> class="Icon <?php echo $flag; ?>Ticket" title="<?php echo ucfirst($flag); ?> Ticket" <?php } ?>
+                    href="tasks.php?id=<?php echo $T['id']; ?>"><?php
+                    echo $title; ?></a>
+                     <?php
+                        if ($threadcount>1)
+                            echo "<small>($threadcount)</small>&nbsp;".'<i
+                                class="icon-fixed-width icon-comments-alt"></i>&nbsp;';
+                        if ($T['collab_count'])
+                            echo '<i class="icon-fixed-width icon-group faded"></i>&nbsp;';
+                        if ($T['attachment_count'])
+                            echo '<i class="icon-fixed-width icon-paperclip"></i>&nbsp;';
+                    ?>
+                </td>
+                <td nowrap>&nbsp;<?php echo Format::truncate($dept, 40); ?></td>
+                <td nowrap>&nbsp;<?php echo $assignee; ?></td>
+            </tr>
+            <?php
+            } //end of foreach
+        if (!$total)
+            $ferror=__('There are no tickets matching your criteria.');
+        ?>
+    </tbody>
+    <tfoot>
+     <tr>
+        <td colspan="6">
+            <?php if($total && $thisstaff->canManageTickets()){ ?>
+            <?php echo __('Select');?>:&nbsp;
+            <a id="selectAll" href="#ckb"><?php echo __('All');?></a>&nbsp;&nbsp;
+            <a id="selectNone" href="#ckb"><?php echo __('None');?></a>&nbsp;&nbsp;
+            <a id="selectToggle" href="#ckb"><?php echo __('Toggle');?></a>&nbsp;&nbsp;
+            <?php }else{
+                echo '<i>';
+                echo $ferror?Format::htmlchars($ferror):__('Query returned 0 results.');
+                echo '</i>';
+            } ?>
+        </td>
+     </tr>
+    </tfoot>
+    </table>
+    <?php
+    if ($total>0) { //if we actually had any tickets returned.
+        echo '<div>&nbsp;'.__('Page').':'.$pageNav->getPageLinks().'&nbsp;';
+        echo sprintf('<a class="export-csv no-pjax" href="?%s">%s</a>',
+                Http::build_query(array(
+                        'a' => 'export', 'h' => $hash,
+                        'status' => $_REQUEST['status'])),
+                __('Export'));
+        echo '&nbsp;<i class="help-tip icon-question-sign" href="#export"></i></div>';
+    } ?>
+    </form>
+</div>
+
+<div style="display:none;" class="dialog" id="confirm-action">
+    <h3><?php echo __('Please Confirm');?></h3>
+    <a class="close" href=""><i class="icon-remove-circle"></i></a>
+    <hr/>
+    <p class="confirm-action" style="display:none;" id="mark_overdue-confirm">
+        <?php echo __('Are you sure want to flag the selected tickets as <font color="red"><b>overdue</b></font>?');?>
+    </p>
+    <div><?php echo __('Please confirm to continue.');?></div>
+    <hr style="margin-top:1em"/>
+    <p class="full-width">
+        <span class="buttons pull-left">
+            <input type="button" value="<?php echo __('No, Cancel');?>" class="close">
+        </span>
+        <span class="buttons pull-right">
+            <input type="button" value="<?php echo __('Yes, Do it!');?>" class="confirm">
+        </span>
+     </p>
+    <div class="clear"></div>
+</div>
+<script type="text/javascript">
+$(function() {
+    $(document).off('.tasks');
+    $(document).on('click.tasks', 'a.tasks-action', function(e) {
+        e.preventDefault();
+        var count = checkbox_checker($('form#tasks'), 1);
+        if (count) {
+            var url = 'ajax.php/'
+            +$(this).attr('href').substr(1)
+            +'?count='+count
+            +'&_uid='+new Date().getTime();
+            $.dialog(url, [201], function (xhr) {
+                $.pjax.reload('#pjax-container');
+             });
+        }
+        return false;
+    });
+    $(document).off('.task-action');
+    $(document).on('click.task-action', 'a.task-action', function(e) {
+        e.preventDefault();
+        var url = 'ajax.php/'
+        +$(this).attr('href').substr(1)
+        +'?_uid='+new Date().getTime();
+        var $options = $(this).data('dialog');
+        var $redirect = $(this).data('redirect');
+        $.dialog(url, [201], function (xhr) {
+            if ($redirect)
+                window.location.href = $redirect;
+            else
+                $.pjax.reload('#pjax-container');
+        }, $options);
+
+        return false;
+    });
+});
+</script>
diff --git a/include/staff/templates/dynamic-form-simple.tmpl.php b/include/staff/templates/dynamic-form-simple.tmpl.php
index cd53f0cd8950126c08f27e18f11f09eb7f92ce0e..8ec0cb2726a6c9b0b33c4d90c5a069938549e920 100644
--- a/include/staff/templates/dynamic-form-simple.tmpl.php
+++ b/include/staff/templates/dynamic-form-simple.tmpl.php
@@ -1,33 +1,43 @@
-<?php
-        echo $form->getMedia();
-        foreach ($form->getFields() as $name=>$f) { ?>
-            <div class="flush-left custom-field" id="field<?php echo $f->getWidget()->id;
-                ?>" <?php if (!$f->isVisible()) echo 'style="display:none;"'; ?>>
-            <div class="field-label <?php if ($f->get('required')) echo 'required'; ?>">
-            <label for="<?php echo $f->getWidget()->name; ?>">
-      <?php if ($f->get('label')) { ?>
-                <?php echo Format::htmlchars($f->get('label')); ?>:
-      <?php } ?>
-      <?php if ($f->get('required')) { ?>
-                <span class="error">*</span>
-      <?php } ?>
-            </label>
-            <?php
-            if ($f->get('hint')) { ?>
-                <br/><em style="color:gray;display:inline-block"><?php
-                    echo Format::viewableImages($f->get('hint')); ?></em>
-            <?php
-            } ?>
-            </div><div>
-            <?php
-            $f->render();
-            ?>
-            </div>
+<div class="form-simple">
+    <?php
+    echo $form->getMedia();
+    foreach ($form->getFields() as $name=>$f) { ?>
+        <div class="flush-left custom-field" id="field<?php echo $f->getWidget()->id;
+            ?>" <?php if (!$f->isVisible()) echo 'style="display:none;"'; ?>>
+        <div class="field-label <?php if ($f->get('required')) echo 'required'; ?>">
+        <label for="<?php echo $f->getWidget()->name; ?>">
+  <?php if ($f->get('label')) { ?>
+            <?php echo Format::htmlchars($f->get('label')); ?>:
+  <?php } ?>
+  <?php if ($f->get('required')) { ?>
+            <span class="error">*</span>
+  <?php } ?>
+        </label>
+        <?php
+        if ($f->get('hint')) { ?>
+            <em style="color:gray;display:block"><?php
+                echo Format::viewableImages($f->get('hint')); ?></em>
+        <?php
+        } ?>
+        </div><div>
+        <?php
+        $f->render($options);
+        ?>
+        </div>
+        <?php
+        if ($f->errors()) { ?>
+            <div id="field<?php echo $f->getWidget()->id; ?>_error">
             <?php
             foreach ($f->errors() as $e) { ?>
                 <div class="error"><?php echo $e; ?></div>
-            <?php } ?>
+            <?php
+            } ?>
             </div>
         <?php
-        }
-?>
+        } ?>
+        </div>
+    <?php
+    }
+    $form->emitJavascript($options);
+    ?>
+</div>
diff --git a/include/staff/templates/dynamic-form.tmpl.php b/include/staff/templates/dynamic-form.tmpl.php
index 7fac7eb1209eb21ad978e27c937c02c24d165e1f..ef18c8bd01c5a988f063d80caea823da0f857ca1 100644
--- a/include/staff/templates/dynamic-form.tmpl.php
+++ b/include/staff/templates/dynamic-form.tmpl.php
@@ -65,7 +65,7 @@ if (isset($options['entry']) && $options['mode'] == 'edit') { ?>
                 <span class="error">*</span>
             <?php
             }
-            if (($a = $field->getAnswer()) && $a->isDeleted()) {
+            if ($field->isStorable() && ($a = $field->getAnswer()) && $a->isDeleted()) {
                 ?><a class="action-button float-right danger overlay" title="Delete this data"
                     href="#delete-answer"
                     onclick="javascript:if (confirm('<?php echo __('You sure?'); ?>'))
diff --git a/include/staff/templates/sub-navigation.tmpl.php b/include/staff/templates/sub-navigation.tmpl.php
index 0cb5e6ed4737ebdfb67e08f80c716b665ddf0d6e..6e40d33c8274b9fd29bf85973c3dc968d17118c7 100644
--- a/include/staff/templates/sub-navigation.tmpl.php
+++ b/include/staff/templates/sub-navigation.tmpl.php
@@ -17,7 +17,13 @@ if(($subnav=$nav->getSubMenu()) && is_array($subnav)){
         if (!($id=$item['id']))
             $id="subnav$k";
 
-        echo sprintf('<li><a class="%s" href="%s" title="%s" id="%s">%s</a></li>',
-                $class, $item['href'], $item['title'], $id, $item['desc']);
+        //Extra attributes
+        $attr = '';
+        if ($item['attr'])
+            foreach ($item['attr'] as $name => $value)
+                $attr.=  sprintf("%s='%s' ", $name, $value);
+
+        echo sprintf('<li><a class="%s" href="%s" title="%s" id="%s" %s>%s</a></li>',
+                $class, $item['href'], $item['title'], $id, $attr, $item['desc']);
     }
 }
diff --git a/include/staff/templates/task-assign.tmpl.php b/include/staff/templates/task-assign.tmpl.php
index 0c5dd7a5eac411e9f78ab99e0096a0dd671187a8..7019fa49a2b1d12fdb03039921f0f65b7f9d367c 100644
--- a/include/staff/templates/task-assign.tmpl.php
+++ b/include/staff/templates/task-assign.tmpl.php
@@ -1,14 +1,13 @@
 <?php
 global $cfg;
 
-if (!$info['title'])
-    $info['title'] = sprintf(__('%s Tasks #%s'),
-            $task->isAssigned() ? __('Reassign') :  __('Assign'),
-            $task->getNumber()
-            );
+$form = $form ?: AssignmentForm::instantiate($info);
 
+if (!$info[':title'])
+    $info[':title'] = sprintf(__('%s Selected Tasks'),
+            __('Assign'));
 ?>
-<h3><?php echo $info['title']; ?></h3>
+<h3><?php echo $info[':title']; ?></h3>
 <b><a class="close" href="#"><i class="icon-remove-circle"></i></a></b>
 <div class="clear"></div>
 <hr/>
@@ -25,17 +24,19 @@ if ($info['error']) {
 }
 
 
-$action = $info['action'] ?: ('#tasks/'.$task->getId().'/assign');
+$action = $info[':action'] ?: ('#tasks/mass/assign');
 ?>
-<div id="ticket-status" style="display:block; margin:5px;">
-<form method="post" name="transfer" id="transfer"
+<div style="display:block; margin:5px;">
+<form class="mass-action" method="post"
+    name="assign"
+    id="<?php echo $form->getId(); ?>"
     action="<?php echo $action; ?>">
     <table width="100%">
         <?php
-        if ($info['extra']) {
+        if ($info[':extra']) {
             ?>
         <tbody>
-            <tr><td colspan="2"><strong><?php echo $info['extra'];
+            <tr><td colspan="2"><strong><?php echo $info[':extra'];
             ?></strong></td> </tr>
         </tbody>
         <?php
@@ -43,40 +44,12 @@ $action = $info['action'] ?: ('#tasks/'.$task->getId().'/assign');
        ?>
         <tbody>
             <tr><td colspan=2>
-                <span>
-                <strong><?php echo __('Agent') ?>:&nbsp;</strong>
-                <select name="staff_id">
-                <?php
-                foreach (Staff::getAvailableStaffMembers() as $id => $name) {
-                    echo sprintf('<option value="%d" %s>%s</option>',
-                            $id,
-                            ($info['staff_id'] == $id)
-                             ? 'selected="selected"' : '',
-                            $name
-                            );
-                }
-                ?>
-                </select>
-                <font class="error">*&nbsp;<?php echo
-                $errors['dept_id']; ?></font>
-                </span>
+             <?php
+             $options = array('template' => 'simple', 'form_id' => 'assign');
+             $form->render($options);
+             ?>
             </td> </tr>
         </tbody>
-        <tbody>
-            <tr>
-                <td colspan="2">
-                <?php
-                $placeholder = $info['placeholder'] ?: __('Optional reason for the assignment');
-                ?>
-                <textarea name="comments" id="comments"
-                    cols="50" rows="3" wrap="soft" style="width:100%"
-                    class="<?php if ($cfg->isHtmlThreadEnabled()) echo 'richtext';
-                    ?> no-bar"
-                    placeholder="<?php echo $placeholder; ?>"><?php
-                    echo $info['comments']; ?></textarea>
-                </td>
-            </tr>
-        </tbody>
     </table>
     <hr>
     <p class="full-width">
diff --git a/include/staff/templates/task-delete.tmpl.php b/include/staff/templates/task-delete.tmpl.php
index 6014bd88aa6be37d1027d01cbb083ba6b4d03b1b..9a9191e4cc5134849fee8d60a5b4e0aa5d82c21a 100644
--- a/include/staff/templates/task-delete.tmpl.php
+++ b/include/staff/templates/task-delete.tmpl.php
@@ -1,14 +1,11 @@
 <?php
 global $cfg;
-
-if (!$info['title'])
-    $info['title'] = sprintf(__('%s Tasks #%s'),
+if (!$info[':title'])
+    $info[':title'] = sprintf(__('%s %s'),
             __('Delete'),
-            $task->getNumber()
-            );
-
+            _N('selected task', 'selected tasks', $count));
 ?>
-<h3><?php echo $info['title']; ?></h3>
+<h3><?php echo $info[':title']; ?></h3>
 <b><a class="close" href="#"><i class="icon-remove-circle"></i></a></b>
 <div class="clear"></div>
 <hr/>
@@ -25,17 +22,18 @@ if ($info['error']) {
 }
 
 
-$action = $info['action'] ?: ('#tasks/'.$task->getId().'/delete');
+$action = $info[':action'] ?: ('#tasks/mass/delete');
 ?>
 <div id="ticket-status" style="display:block; margin:5px;">
 <form method="post" name="delete" id="delete"
-    action="<?php echo $action; ?>">
+    action="<?php echo $action; ?>"
+    class="mass-action">
     <table width="100%">
         <?php
-        if ($info['extra']) {
+        if ($info[':extra']) {
             ?>
         <tbody>
-            <tr><td colspan="2"><strong><?php echo $info['extra'];
+            <tr><td colspan="2"><strong><?php echo $info[':extra'];
             ?></strong></td> </tr>
         </tbody>
         <?php
@@ -45,7 +43,7 @@ $action = $info['action'] ?: ('#tasks/'.$task->getId().'/delete');
             <tr>
                 <td colspan="2">
                 <?php
-                $placeholder = $info['placeholder'] ?: __('Optional reason for the deletion');
+                $placeholder = $info[':placeholder'] ?: __('Optional reason for the deletion');
                 ?>
                 <textarea name="comments" id="comments"
                     cols="50" rows="3" wrap="soft" style="width:100%"
diff --git a/include/staff/templates/task-status.tmpl.php b/include/staff/templates/task-status.tmpl.php
new file mode 100644
index 0000000000000000000000000000000000000000..b711e6f91c71f2e8b46a6d353ca5ef2e859d5e39
--- /dev/null
+++ b/include/staff/templates/task-status.tmpl.php
@@ -0,0 +1,101 @@
+<?php
+global $cfg;
+
+if (!$info[':title'])
+    $info[':title'] = __('Change Tasks Status');
+
+?>
+<h3><?php echo $info[':title']; ?></h3>
+<b><a class="close" href="#"><i class="icon-remove-circle"></i></a></b>
+<div class="clear"></div>
+<hr/>
+<?php
+if ($info['error']) {
+    echo sprintf('<p id="msg_error">%s</p>', $info['error']);
+} elseif ($info['warn']) {
+    echo sprintf('<p id="msg_warning">%s</p>', $info['warn']);
+} elseif ($info['msg']) {
+    echo sprintf('<p id="msg_notice">%s</p>', $info['msg']);
+} elseif ($info['notice']) {
+   echo sprintf('<p id="msg_info"><i class="icon-info-sign"></i> %s</p>',
+           $info['notice']);
+}
+
+$action = $info[':action'] ?: ('#tasks/mass/'. $action);
+?>
+<div style="display:block; margin:5px;">
+    <form method="post" name="status" id="status"
+        action="<?php echo $action; ?>"
+        class="mass-action">
+        <table width="100%">
+            <?php
+            if ($info[':extra']) {
+                ?>
+            <tbody>
+                <tr><td colspan="2"><strong><?php echo $info[':extra'];
+                ?></strong></td> </tr>
+            </tbody>
+            <?php
+            }
+            ?>
+            <tbody>
+                <tr>
+                    <td colspan=2>
+                        <span>
+                            <strong><?php echo __('Status') ?>:&nbsp;</strong>
+                            <select name="status">
+                            <?php
+                            $statuses = array(
+                                    'open' => __('Open'),
+                                    'closed' => __('Closed'));
+
+                            if (!$info['status'])
+                                echo '<option value=""> '. __('Select One')
+                                .' </option>';
+                            foreach ($statuses as $k => $status) {
+                                echo sprintf('<option value="%s" %s>%s</option>',
+                                        $k,
+                                        ($info['status'] == $k)
+                                         ? 'selected="selected"' : '',
+                                        $status
+                                        );
+                            }
+                            ?>
+                            </select>
+                            <font class="error">*&nbsp;<?php echo
+                            $errors['status']; ?></font>
+                        </span>
+                    </td>
+                </tr>
+            </tbody>
+            <tbody>
+                <tr>
+                    <td colspan="2">
+                        <?php
+                        $placeholder = $info[':placeholder'] ?: __('Optional reason for status change (internal note)');
+                        ?>
+                        <textarea name="comments" id="comments"
+                            cols="50" rows="3" wrap="soft" style="width:100%"
+                            class="<?php if ($cfg->isHtmlThreadEnabled()) echo 'richtext';
+                            ?> no-bar"
+                            placeholder="<?php echo $placeholder; ?>"><?php
+                            echo $info['comments']; ?></textarea>
+                    </td>
+                </tr>
+            </tbody>
+        </table>
+        <hr>
+        <p class="full-width">
+            <span class="buttons pull-left">
+                <input type="reset" value="<?php echo __('Reset'); ?>">
+                <input type="button" name="cancel" class="close"
+                value="<?php echo __('Cancel'); ?>">
+            </span>
+            <span class="buttons pull-right">
+                <input type="submit" value="<?php
+                echo $verb ?: __('Submit'); ?>">
+            </span>
+         </p>
+    </form>
+</div>
+<div class="clear"></div>
diff --git a/include/staff/templates/task-transfer.tmpl.php b/include/staff/templates/task-transfer.tmpl.php
index 8a872f43845ec328c92554f32125e10682173b89..a45311f7f991b4fba4e0522272401edde44c376b 100644
--- a/include/staff/templates/task-transfer.tmpl.php
+++ b/include/staff/templates/task-transfer.tmpl.php
@@ -1,12 +1,13 @@
 <?php
 global $cfg;
 
-if (!$info['title'])
-    $info['title'] = sprintf(__('%s Tasks #%s'),
-            __('Tranfer'), $task->getNumber());
+$form = $form ?: TransferForm::instantiate($info);
 
+if (!$info[':title'])
+    $info[':title'] = sprintf(__('%s Selected Tasks'),
+            __('Tranfer'));
 ?>
-<h3><?php echo $info['title']; ?></h3>
+<h3><?php echo $info[':title']; ?></h3>
 <b><a class="close" href="#"><i class="icon-remove-circle"></i></a></b>
 <div class="clear"></div>
 <hr/>
@@ -23,17 +24,18 @@ if ($info['error']) {
 }
 
 
-$action = $info['action'] ?: ('#tasks/'.$task->getId().'/transfer');
+$action = $info[':action'] ?: ('#tasks/mass/transfer');
 ?>
 <div style="display:block; margin:5px;">
 <form method="post" name="transfer" id="transfer"
+    class="mass-action"
     action="<?php echo $action; ?>">
     <table width="100%">
         <?php
-        if ($info['extra']) {
+        if ($info[':extra']) {
             ?>
         <tbody>
-            <tr><td colspan="2"><strong><?php echo $info['extra'];
+            <tr><td colspan="2"><strong><?php echo $info[':extra'];
             ?></strong></td> </tr>
         </tbody>
         <?php
@@ -41,40 +43,12 @@ $action = $info['action'] ?: ('#tasks/'.$task->getId().'/transfer');
        ?>
         <tbody>
             <tr><td colspan=2>
-                <span>
-                <strong><?php echo __('Department') ?>:&nbsp;</strong>
-                <select name="dept_id">
-                <?php
-                foreach (Dept::getDepartments() as $id => $name) {
-                    echo sprintf('<option value="%d" %s>%s</option>',
-                            $id,
-                            ($info['dept_id'] == $id)
-                             ? 'selected="selected"' : '',
-                            $name
-                            );
-                }
-                ?>
-                </select>
-                <font class="error">*&nbsp;<?php echo
-                $errors['dept_id']; ?></font>
-                </span>
+             <?php
+             $options = array('template' => 'simple', 'form_id' => 'transfer');
+             $form->render($options);
+             ?>
             </td> </tr>
         </tbody>
-        <tbody>
-            <tr>
-                <td colspan="2">
-                <?php
-                $placeholder = $info['placeholder'] ?: __('Optional reason for the transfer');
-                ?>
-                <textarea name="comments" id="comments"
-                    cols="50" rows="3" wrap="soft" style="width:100%"
-                    class="<?php if ($cfg->isHtmlThreadEnabled()) echo 'richtext';
-                    ?> no-bar"
-                    placeholder="<?php echo $placeholder; ?>"><?php
-                    echo $info['comments']; ?></textarea>
-                </td>
-            </tr>
-        </tbody>
     </table>
     <hr>
     <p class="full-width">
diff --git a/include/staff/templates/task-view.tmpl.php b/include/staff/templates/task-view.tmpl.php
index a9a4c72170780e61cd2edd7ada25a4ab30a6ab3b..7cd928b8a15e05921fe7e612fffd2e477fde94a2 100644
--- a/include/staff/templates/task-view.tmpl.php
+++ b/include/staff/templates/task-view.tmpl.php
@@ -1,50 +1,60 @@
 <?php
 if (!defined('OSTSCPINC')
-        || !$thisstaff
-        || !$task
-        || !($role = $thisstaff->getRole($task->getId())))
+    || !$thisstaff || !$task
+    || !($role = $thisstaff->getRole($task->getDeptId())))
     die('Invalid path');
 
-
 $actions = array();
 
+$actions += array(
+        'print' => array(
+            'href' => sprintf('tasks.php?id=%d&a=print', $task->getId()),
+            'class' => 'none',
+            'icon' => 'icon-print',
+            'label' => __('Print')
+        ));
+
 if ($role->hasPerm(Task::PERM_EDIT)) {
     $actions += array(
             'edit' => array(
+                'href' => sprintf('#tasks/%d/edit', $task->getId()),
                 'icon' => 'icon-edit',
                 'dialog' => '{"size":"large"}',
-                'action' => __('Edit')
+                'label' => __('Edit')
             ));
 }
 
 if ($role->hasPerm(Task::PERM_ASSIGN)) {
     $actions += array(
             'assign' => array(
+                'href' => sprintf('#tasks/%d/assign', $task->getId()),
                 'icon' => 'icon-user',
-                'action' => $task->isAssigned() ? __('Reassign') : __('Assign')
+                'label' => $task->isAssigned() ? __('Reassign') : __('Assign')
             ));
 }
 
 if ($role->hasPerm(Task::PERM_TRANSFER)) {
     $actions += array(
             'transfer' => array(
+                'href' => sprintf('#tasks/%d/transfer', $task->getId()),
                 'icon' => 'icon-share',
-                'action' => __('Transfer')
+                'label' => __('Transfer')
             ));
 }
 
+
 if ($role->hasPerm(Task::PERM_DELETE)) {
     $actions += array(
             'delete' => array(
+                'href' => sprintf('#tasks/%d/delete', $task->getId()),
                 'icon' => 'icon-trash',
-                'action' => __('Delete')
+                'label' => __('Delete')
             ));
 }
 
-
 $info=($_POST && $errors)?Format::input($_POST):array();
 
-$id    = $task->getId();    //Ticket ID.
+$id    = $task->getId();
 if ($task->isOverdue())
     $warn.='&nbsp;&nbsp;<span class="Icon overdueTicket">'.__('Marked overdue!').'</span>';
 
@@ -52,214 +62,198 @@ if ($task->isOverdue())
 <table width="940" cellpadding="2" cellspacing="0" border="0">
     <tr>
         <td width="20%" class="has_bottom_border">
-            <h3><a href="#tasks/<?php echo $task->getId(); ?>/view"
-                    id="reload-task"><i class="icon-refresh"></i> <?php
+            <h2><a
+                id="reload-task"
+                href="tasks.php?id=<?php echo $task->getId(); ?>"
+                <?php
+                if ($ticket) {
+                    echo ' class="preview" ';
+                    echo sprintf('data-preview="#tasks/%d/preview" ', $task->getId());
+                    echo sprintf('href="#tasks/%d" ', $task->getId());
+                } else { ?>
+                    href="tasks.php?id=<?php echo $task->getId(); ?>"
+                <?php
+                } ?>
+                ><i class="icon-refresh"></i> <?php
                 echo sprintf(__('Task #%s'), $task->getNumber()); ?></a>
-            </h3>
+            </h2>
         </td>
         <td width="auto" class="flush-right has_bottom_border">
-        <?php
-           if ($actions) { ?>
+            <?php
+            if ($ticket) { ?>
             <span
                 class="action-button"
-                data-dropdown="#action-dropdown-taskoptions">
+                data-dropdown="#action-dropdown-task-options">
                 <i class="icon-caret-down pull-right"></i>
                 <a class="task-action"
-                    href="#taskoptions"><i
+                    href="#task-options"><i
                     class="icon-reorder"></i> <?php
-                    echo __('Task Options'); ?></a>
+                    echo __('Options'); ?></a>
             </span>
-            <div id="action-dropdown-taskoptions"
+            <div id="action-dropdown-task-options"
                 class="action-dropdown anchor-right">
                 <ul>
             <?php foreach ($actions as $a => $action) { ?>
                     <li>
-                        <a class="no-pjax task-action"
+                        <a class="no-pjax <?php
+                            echo $action['class'] ?: 'task-action'; ?>"
                             <?php
                             if ($action['dialog'])
                                 echo sprintf("data-dialog='%s'", $action['dialog']);
+                            if ($action['redirect'])
+                                echo sprintf("data-redirect='%s'", $action['redirect']);
                             ?>
-                            href="<?php
-                            echo sprintf('#tasks/%d/%s', $task->getId(), $a); ?>"
+                            href="<?php echo $action['href']; ?>"
                             ><i class="<?php
                             echo $action['icon'] ?: 'icon-tag'; ?>"></i> <?php
-                            echo $action['action']; ?></a>
+                            echo  $action['label']; ?></a>
                     </li>
                 <?php
                 } ?>
                 </ul>
             </div>
             <?php
-           } ?>
-        </td>
-    </tr>
-</table>
-<table class="ticket_info" cellspacing="0" cellpadding="0" width="940" border="0">
-    <tr>
-        <td width="50%">
-            <table border="0" cellspacing="" cellpadding="4" width="100%">
-                <tr>
-                    <th width="100"><?php echo __('Status');?>:</th>
-                    <td><?php echo $task->getStatus(); ?></td>
-                </tr>
-                <tr>
-                    <th><?php echo __('Department');?>:</th>
-                    <td><?php echo Format::htmlchars($task->dept->getName()); ?></td>
-                </tr>
-                <tr>
-                    <th><?php echo __('Create Date');?>:</th>
-                    <td><?php echo Format::datetime($task->getCreateDate()); ?></td>
-                </tr>
-            </table>
-        </td>
-        <td width="50%" style="vertical-align:top">
-            <table cellspacing="0" cellpadding="4" width="100%" border="0">
-                <?php
-                if ($task->isOpen()) { ?>
-                <tr>
-                    <th width="100"><?php echo __('Assigned To');?>:</th>
-                    <td>
-                        <?php
-                        if ($assigned=$task->getAssigned())
-                            echo Format::htmlchars($assigned);
-                        else
-                            echo '<span class="faded">&mdash; '.__('Unassigned').' &mdash;</span>';
-                        ?>
-                    </td>
-                </tr>
-                <?php
-                } else { ?>
-                <tr>
-                    <th width="100"><?php echo __('Closed By');?>:</th>
-                    <td>
-                        <?php
-                        if (0 && ($staff = $task->getStaff()))
-                            echo Format::htmlchars($staff->getName());
-                        else
-                            echo '<span class="faded">&mdash; '.__('Unknown').' &mdash;</span>';
-                        ?>
-                    </td>
-                </tr>
-                <?php
-                } ?>
-                <tr>
-                    <th><?php echo __('SLA Plan');?>:</th>
-                    <td><?php echo $sla?Format::htmlchars($sla->getName()):'<span class="faded">&mdash; '.__('None').' &mdash;</span>'; ?></td>
-                </tr>
-                <?php
-                if($task->isOpen()){ ?>
-                <tr>
-                    <th><?php echo __('Due Date');?>:</th>
-                    <td><?php echo $task->duedate ?
-                    Format::datetime($task->duedate) : '<span
-                    class="faded">&mdash; '.__('None').' &mdash;</span>'; ?></td>
-                </tr>
-                <?php
-                }else { ?>
-                <tr>
-                    <th><?php echo __('Close Date');?>:</th>
-                    <td><?php echo 0 ?
-                    Format::datetime($task->getCloseDate()) : ''; ?></td>
-                </tr>
-                <?php
+           } else {
+                foreach ($actions as $action) {?>
+                <a class="action-button <?php
+                    echo $action['class'] ?: 'task-action'; ?>"
+                    <?php
+                    if ($action['dialog'])
+                        echo sprintf("data-dialog='%s'", $action['dialog']);
+                    ?>
+                    href="<?php echo $action['href']; ?>"><i
+                    class="<?php
+                    echo $action['icon'] ?: 'icon-tag'; ?>"></i> <?php
+                    echo $action['label'];
+                ?></a>
+           <?php
                 }
-                ?>
-            </table>
+           } ?>
         </td>
     </tr>
 </table>
-<br>
-<br>
-<table class="ticket_info" cellspacing="0" cellpadding="0" width="940" border="0">
+
 <?php
-$idx = 0;
-foreach (DynamicFormEntry::forObject($task->getId(),
-            ObjectModel::OBJECT_TYPE_TASK) as $form) {
-    $answers = array_filter($form->getAnswers(), function ($a) {
-            return $a->getField()->isStorable();
-        });
-    if (count($answers) == 0)
-        continue;
-    ?>
+if (!$ticket) { ?>
+    <table class="ticket_info" cellspacing="0" cellpadding="0" width="940" border="0">
         <tr>
-        <td colspan="2">
-            <table cellspacing="0" cellpadding="4" width="100%" border="0">
-            <?php foreach($answers as $a) {
-                if (!($v = $a->display())) continue; ?>
-                <tr>
-                    <th width="100"><?php
-                        echo $a->getField()->get('label');
-                    ?>:</th>
-                    <td><?php
-                        echo $v;
-                    ?></td>
-                </tr>
-                <?php
-            } ?>
-            </table>
-        </td>
+            <td width="50%">
+                <table border="0" cellspacing="" cellpadding="4" width="100%">
+                    <tr>
+                        <th width="100"><?php echo __('Status');?>:</th>
+                        <td><?php echo $task->getStatus(); ?></td>
+                    </tr>
+                    <tr>
+                        <th><?php echo __('Department');?>:</th>
+                        <td><?php echo Format::htmlchars($task->dept->getName()); ?></td>
+                    </tr>
+                    <?php
+                    if ($task->isOpen()) { ?>
+                    <tr>
+                        <th width="100"><?php echo __('Assigned To');?>:</th>
+                        <td>
+                            <?php
+                            if ($assigned=$task->getAssigned())
+                                echo Format::htmlchars($assigned);
+                            else
+                                echo '<span class="faded">&mdash; '.__('Unassigned').' &mdash;</span>';
+                            ?>
+                        </td>
+                    </tr>
+                    <?php
+                    } else { ?>
+                    <tr>
+                        <th width="100"><?php echo __('Closed By');?>:</th>
+                        <td>
+                            <?php
+                            if (0 && ($staff = $task->getStaff()))
+                                echo Format::htmlchars($staff->getName());
+                            else
+                                echo '<span class="faded">&mdash; '.__('Unknown').' &mdash;</span>';
+                            ?>
+                        </td>
+                    </tr>
+                    <?php
+                    } ?>
+                </table>
+            </td>
+            <td width="50%" style="vertical-align:top">
+                <table cellspacing="0" cellpadding="4" width="100%" border="0">
+                    <tr>
+                        <th><?php echo __('SLA Plan');?>:</th>
+                        <td><?php echo $sla?Format::htmlchars($sla->getName()):'<span class="faded">&mdash; '.__('None').' &mdash;</span>'; ?></td>
+                    </tr>
+                    <tr>
+                        <th><?php echo __('Create Date');?>:</th>
+                        <td><?php echo Format::datetime($task->getCreateDate()); ?></td>
+                    </tr>
+                    <?php
+                    if($task->isOpen()){ ?>
+                    <tr>
+                        <th><?php echo __('Due Date');?>:</th>
+                        <td><?php echo $task->duedate ?
+                        Format::datetime($task->duedate) : '<span
+                        class="faded">&mdash; '.__('None').' &mdash;</span>'; ?></td>
+                    </tr>
+                    <?php
+                    }else { ?>
+                    <tr>
+                        <th><?php echo __('Close Date');?>:</th>
+                        <td><?php echo 0 ?
+                        Format::datetime($task->getCloseDate()) : ''; ?></td>
+                    </tr>
+                    <?php
+                    }
+                    ?>
+                </table>
+            </td>
         </tr>
+    </table>
+    <br>
+    <br>
+    <table class="ticket_info" cellspacing="0" cellpadding="0" width="940" border="0">
     <?php
-    $idx++;
+    $idx = 0;
+    foreach (DynamicFormEntry::forObject($task->getId(),
+                ObjectModel::OBJECT_TYPE_TASK) as $form) {
+        $answers = $form->getAnswers()->exclude(Q::any(array(
+            'field__flags__hasbit' => DynamicFormField::FLAG_EXT_STORED
+        )));
+        if (!$answers || count($answers) == 0)
+            continue;
+
+        ?>
+            <tr>
+            <td colspan="2">
+                <table cellspacing="0" cellpadding="4" width="100%" border="0">
+                <?php foreach($answers as $a) {
+                    if (!($v = $a->display())) continue; ?>
+                    <tr>
+                        <th width="100"><?php
+                            echo $a->getField()->get('label');
+                        ?>:</th>
+                        <td><?php
+                            echo $v;
+                        ?></td>
+                    </tr>
+                    <?php
+                } ?>
+                </table>
+            </td>
+            </tr>
+        <?php
+        $idx++;
+    } ?>
+    </table>
+<?php
 } ?>
-</table>
 <div class="clear"></div>
 <div id="task_thread_container">
-    <div id="task_thread_content">
+    <div id="task_thread_content" data-thread-id="<?php echo
+    $task->getThread()->getId(); ?>" class="tab_content">
     <?php
-    $threadTypes=array('M'=>'message','R'=>'response', 'N'=>'note');
-    /* -------- Messages & Responses & Notes (if inline)-------------*/
-    $types = array('M', 'R', 'N');
-    if(($thread=$task->getThreadEntries($types))) {
-       foreach($thread as $entry) { ?>
-        <table class="thread-entry <?php echo $threadTypes[$entry->type]; ?>" cellspacing="0" cellpadding="1" width="940" border="0">
-            <tr>
-                <th colspan="4" width="100%">
-                <div>
-                    <span class="pull-left">
-                    <span style="display:inline-block"><?php
-                        echo Format::datetime($entry->created);?></span>
-                    <span style="display:inline-block;padding:0 1em" class="faded title"><?php
-                        echo Format::truncate($entry->title, 100); ?></span>
-                    </span>
-                    <span class="pull-right" style="white-space:no-wrap;display:inline-block">
-                        <span style="vertical-align:middle;" class="textra"></span>
-                        <span style="vertical-align:middle;"
-                            class="tmeta faded title"><?php
-                            echo Format::htmlchars($entry->getName()); ?></span>
-                    </span>
-                </div>
-                </th>
-            </tr>
-            <tr><td colspan="4" class="thread-body" id="thread-id-<?php
-                echo $entry->getId(); ?>"><div><?php
-                echo $entry->getBody()->toHtml(); ?></div></td></tr>
-            <?php
-            $urls = null;
-            if($entry->has_attachments
-                    && ($urls = $tentry->getAttachmentUrls())
-                    && ($links = $tentry->getAttachmentsLinks())) {?>
-            <tr>
-                <td class="info" colspan="4"><?php echo $links; ?></td>
-            </tr> <?php
-            }
-            if ($urls) { ?>
-                <script type="text/javascript">
-                    $('#thread-id-<?php echo $entry->getId(); ?>')
-                        .data('urls', <?php
-                            echo JsonDataEncoder::encode($urls); ?>)
-                        .data('id', <?php echo $entry['id']; ?>);
-                </script>
-<?php
-            } ?>
-        </table>
-        <?php
-        if ($entry->type == 'M')
-            $msgId = $entry->getId();
-       }
-    } else {
-        echo '<p>'.__('Error fetching thread - get technical help.').'</p>';
-    }?>
+    $task->getThread()->render(array('M', 'R', 'N'));
+    ?>
    </div>
 </div>
 <div class="clear" style="padding-bottom:10px;"></div>
@@ -269,11 +263,18 @@ foreach (DynamicFormEntry::forObject($task->getId(),
     <div id="msg_notice"><?php echo $msg; ?></div>
 <?php }elseif($warn) { ?>
     <div id="msg_warning"><?php echo $warn; ?></div>
-<?php } ?>
+<?php }
+
+if ($ticket)
+    $action = sprintf('#tickets/%d/tasks/%d',
+            $ticket->getId(), $task->getId());
+else
+    $action = 'tasks.php?id='.$task->getId();
+?>
 <div id="response_options">
     <ul class="tabs"></ul>
-    <form id="task_note"
-        action="#tasks/<?php echo $task->getId(); ?>"
+    <form id="<?php echo $ticket? 'ticket_task_note': 'task_note'; ?>"
+        action="<?php echo $action; ?>"
         name="task_note"
         method="post" enctype="multipart/form-data">
         <?php csrf_token(); ?>
@@ -301,8 +302,8 @@ foreach (DynamicFormEntry::forObject($task->getId(),
                         ?></textarea>
                     <div class="attachments">
                     <?php
-                        if ($task_note_form)
-                            print $task_note_form->getField('attachments')->render();
+                        if ($note_form)
+                            print $note_form->getField('attachments')->render();
                     ?>
                     </div>
                 </td>
@@ -333,6 +334,7 @@ foreach (DynamicFormEntry::forObject($task->getId(),
        </p>
     </form>
  </div>
+
 <script type="text/javascript">
 $(function() {
     $(document).on('click', 'li.active a#ticket_tasks', function(e) {
@@ -341,8 +343,27 @@ $(function() {
         $('div#tasks_content').show();
         return false;
      });
+
+    $(document).off('.task-action');
+    $(document).on('click.task-action', 'a.task-action', function(e) {
+        e.preventDefault();
+        var url = 'ajax.php/'
+        +$(this).attr('href').substr(1)
+        +'?_uid='+new Date().getTime();
+        var $options = $(this).data('dialog');
+        var $redirect = $(this).data('redirect');
+        $.dialog(url, [201], function (xhr) {
+            if ($redirect)
+                window.location.href = $redirect;
+            else
+                $.pjax.reload('#pjax-container');
+        }, $options);
+
+        return false;
+    });
+
     $(document).off('.tf');
-    $(document).on('submit.tf', 'form#task_note', function(e) {
+    $(document).on('submit.tf', 'form#ticket_task_note', function(e) {
         e.preventDefault();
         var $form = $(this);
         var $container = $('div#task_content');
@@ -360,6 +381,6 @@ $(function() {
         })
         .done(function() { })
         .fail(function() { });
-    });
+     });
 });
 </script>
diff --git a/include/staff/templates/task.tmpl.php b/include/staff/templates/task.tmpl.php
index bf1355bc0ac77b94c672f322eee7e1389674ccf4..d92da014f9f53e7367f67daecbfe3f41b517c45b 100644
--- a/include/staff/templates/task.tmpl.php
+++ b/include/staff/templates/task.tmpl.php
@@ -3,6 +3,10 @@
 if (!$info['title'])
     $info['title'] = __('New Task');
 
+$namespace = 'task.add';
+if ($ticket)
+    $namespace = sprintf('ticket.%d.task', $ticket->getId());
+
 ?>
 <div id="task-form">
 <h3><?php echo $info['title']; ?></h3>
@@ -24,9 +28,7 @@ if ($info['error']) {
         $form = $form ?: TaskForm::getInstance();
         $form->render(true,
                 __('Create New Task'),
-                array(
-                    'draft-namespace' => sprintf('ticket.%d.task',
-                        $ticket->getId()))
+                array('draft-namespace' => $namespace)
                 );
     ?>
         <tr><th colspan=2><em><?php
diff --git a/include/staff/templates/tasks-actions.tmpl.php b/include/staff/templates/tasks-actions.tmpl.php
new file mode 100644
index 0000000000000000000000000000000000000000..306cd6ea9ab29c390f4dd01c3bca570890b7a2fe
--- /dev/null
+++ b/include/staff/templates/tasks-actions.tmpl.php
@@ -0,0 +1,139 @@
+<?php
+// Tasks' mass actions based on logged in agent
+
+$actions = array();
+
+if ($agent->hasPerm(Task::PERM_CLOSE)) {
+
+    if (isset($options['status'])) {
+        $status = $options['status'];
+    ?>
+        <span
+            class="action-button"
+            data-dropdown="#action-dropdown-tasks-status">
+            <i class="icon-caret-down pull-right"></i>
+            <a class="tasks-status-action"
+                href="#statuses"><i
+                class="icon-flag"></i> <?php
+                echo __('Change Status'); ?></a>
+        </span>
+        <div id="action-dropdown-tasks-status"
+            class="action-dropdown anchor-right">
+            <ul>
+                <?php
+                if (!$status || !strcasecmp($status, 'closed')) { ?>
+                <li>
+                    <a class="no-pjax tasks-action"
+                        href="#tasks/mass/reopen"><i
+                        class="icon-fixed-width icon-undo"></i> <?php
+                        echo __('Reopen Tasks');?> </a>
+                </li>
+                <?php
+                }
+                if (!$status || !strcasecmp($status, 'open')) {
+                ?>
+                <li>
+                    <a class="no-pjax tasks-action"
+                        href="#tasks/mass/close"><i
+                        class="icon-fixed-width icon-ok-circle"></i> <?php
+                        echo __('Close Tasks');?> </a>
+                </li>
+                <?php
+                } ?>
+            </ul>
+        </div>
+<?php
+    } else {
+
+        $actions += array(
+                'reopen' => array(
+                    'icon' => 'icon-undo',
+                    'action' => __('Reopen Tasks')
+                ));
+
+        $actions += array(
+                'close' => array(
+                    'icon' => 'icon-ok-circle',
+                    'action' => __('Close Tasks')
+                ));
+    }
+}
+
+if ($agent->hasPerm(Task::PERM_ASSIGN)) {
+    $actions += array(
+            'assign' => array(
+                'icon' => 'icon-user',
+                'action' => __('Assign Tasks')
+            ));
+}
+
+if ($agent->hasPerm(Task::PERM_TRANSFER)) {
+    $actions += array(
+            'transfer' => array(
+                'icon' => 'icon-share',
+                'action' => __('Transfer Tasks')
+            ));
+}
+
+if ($agent->hasPerm(Task::PERM_DELETE)) {
+    $actions += array(
+            'delete' => array(
+                'icon' => 'icon-trash',
+                'action' => __('Delete Tasks')
+            ));
+}
+if ($actions) {
+    $more = $options['morelabel'] ?: __('More');
+    ?>
+    <span
+        class="action-button"
+        data-dropdown="#action-dropdown-moreoptions">
+        <i class="icon-caret-down pull-right"></i>
+        <a class="tasks-action"
+            href="#moreoptions"><i
+            class="icon-reorder"></i> <?php
+            echo $more; ?></a>
+    </span>
+    <div id="action-dropdown-moreoptions"
+        class="action-dropdown anchor-right">
+        <ul>
+    <?php foreach ($actions as $a => $action) { ?>
+            <li>
+                <a class="no-pjax tasks-action"
+                    <?php
+                    if ($action['dialog'])
+                        echo sprintf("data-dialog='%s'", $action['dialog']);
+                    if ($action['redirect'])
+                        echo sprintf("data-redirect='%s'", $action['redirect']);
+                    ?>
+                    href="<?php
+                    echo sprintf('#tasks/mass/%s', $a); ?>"
+                    ><i class="icon-fixed-width <?php
+                    echo $action['icon'] ?: 'icon-tag'; ?>"></i> <?php
+                    echo $action['action']; ?></a>
+            </li>
+        <?php
+        } ?>
+        </ul>
+    </div>
+ <?php
+ } ?>
+<script type="text/javascript">
+$(function() {
+    $(document).off('.tasks');
+    $(document).on('click.tasks', 'a.tasks-action', function(e) {
+        e.preventDefault();
+        var count = checkbox_checker($('form#tasks'), 1);
+        if (count) {
+            var url = 'ajax.php/'
+            +$(this).attr('href').substr(1)
+            +'?count='+count
+            +'&_uid='+new Date().getTime();
+            $.dialog(url, [201], function (xhr) {
+                $.pjax.reload('#pjax-container');
+             });
+        }
+        return false;
+    });
+});
+</script>
diff --git a/include/staff/templates/thread-entries.tmpl.php b/include/staff/templates/thread-entries.tmpl.php
new file mode 100644
index 0000000000000000000000000000000000000000..821c1c50ca4416860e4350507fa730b744d6a2e9
--- /dev/null
+++ b/include/staff/templates/thread-entries.tmpl.php
@@ -0,0 +1,83 @@
+<?php
+$entryTypes = array('M'=>'message', 'R'=>'response', 'N'=>'note');
+if ($entries) {
+    foreach ($entries as $entry) { ?>
+    <table class="thread-entry <?php echo $entryTypes[$entry->type]; ?>" cellspacing="0" cellpadding="1" width="940" border="0">
+        <tr>
+            <th colspan="4" width="100%">
+            <div>
+                <span class="pull-left">
+                <span style="display:inline-block"><?php
+                    echo Format::datetime($entry->created);?></span>
+                <span style="display:inline-block;padding:0 1em;max-width: 500px" class="faded title truncate"><?php
+                    echo $entry->title; ?></span>
+                </span>
+            <div class="pull-right">
+<?php           if ($entry->hasActions()) {
+                $actions = $entry->getActions(); ?>
+                <span class="action-button pull-right" data-dropdown="#entry-action-more-<?php echo $entry->getId(); ?>">
+                    <i class="icon-caret-down"></i>
+                    <span ><i class="icon-cog"></i></span>
+                </span>
+                <div id="entry-action-more-<?php echo $entry->getId(); ?>" class="action-dropdown anchor-right">
+            <ul class="title">
+<?php               foreach ($actions as $group => $list) {
+                    foreach ($list as $id => $action) { ?>
+                <li>
+                <a class="no-pjax" href="#" onclick="javascript:
+                        <?php echo str_replace('"', '\\"', $action->getJsStub()); ?>; return false;">
+                    <i class="<?php echo $action->getIcon(); ?>"></i> <?php
+                        echo $action->getName();
+            ?></a></li>
+<?php                   }
+                } ?>
+            </ul>
+            </div>
+<?php           } ?>
+                <span style="vertical-align:middle">
+                    <span style="vertical-align:middle;" class="textra"></span>
+                    <span style="vertical-align:middle;"
+                        class="tmeta faded title"><?php
+                        echo Format::htmlchars($entry->getName()); ?></span>
+                </span>
+            </div>
+            </th>
+        </tr>
+        <tr><td colspan="4" class="thread-body" id="thread-id-<?php
+            echo $entry->getId(); ?>"><div><?php
+            echo $entry->getBody()->toHtml(); ?></div></td></tr>
+        <?php
+        $urls = null;
+        if ($entry->has_attachments
+            && ($urls = $entry->getAttachmentUrls())) { ?>
+        <tr>
+            <td class="info" colspan="4"><?php
+                foreach ($entry->attachments as $A) {
+                    if ($A->inline) continue;
+                    $size = '';
+                    if ($A->file->size)
+                        $size = sprintf('<em>(%s)</em>',
+                            Format::file_size($A->file->size));
+?>
+            <a class="Icon file no-pjax" href="<?php echo $A->file->getDownloadUrl();
+                ?>" target="_blank"><?php echo Format::htmlchars($A->file->name);
+            ?></a><?php echo $size;?>&nbsp;
+<?php               } ?>
+            </td>
+        </tr> <?php
+        }
+        if ($urls) { ?>
+            <script type="text/javascript">
+                $('#thread-id-<?php echo $entry->getId(); ?>')
+                    .data('urls', <?php
+                        echo JsonDataEncoder::encode($urls); ?>)
+                    .data('id', <?php echo $entry->getId(); ?>);
+            </script>
+<?php
+        } ?>
+    </table>
+    <?php
+    }
+} else {
+    echo '<p><em>'.__('No entries have been posted to this thread.').'</em></p>';
+}?>
diff --git a/include/staff/ticket-tasks.inc.php b/include/staff/ticket-tasks.inc.php
index 8b8f3fb9d7c13f601ad6c101b4d2f7a32dd0bb43..802ea24989c6da9017493a42e418291722395c4e 100644
--- a/include/staff/ticket-tasks.inc.php
+++ b/include/staff/ticket-tasks.inc.php
@@ -4,9 +4,12 @@ global $thisstaff;
 $role = $thisstaff->getRole($ticket->getDeptId());
 
 $tasks = Task::objects()
-    ->select_related('dept', 'staff')
+    ->select_related('dept', 'staff', 'team')
     ->order_by('-created');
 
+$tasks->filter(array(
+            'object_id' => $ticket->getId(),
+            'object_type' => 'T'));
 
 $count = $tasks->count();
 $pageNav = new Pagenate($count,1, 100000); //TODO: support ajax based pages
@@ -14,7 +17,7 @@ $showing = $pageNav->showing().' '._N('task', 'tasks', $count);
 
 ?>
 <div id="tasks_content" style="display:block;">
-<div style="width:700px; float:left;">
+<div class="pull-left">
    <?php
     if ($count) {
         echo '<strong>'.$showing.'</strong>';
@@ -24,24 +27,28 @@ $showing = $pageNav->showing().' '._N('task', 'tasks', $count);
     }
    ?>
 </div>
-<div style="float:right;text-align:right;padding-right:5px;">
+<div class="pull-right">
     <?php
     if ($role && $role->hasPerm(Task::PERM_CREATE)) { ?>
         <a
-        class="Icon newTicket task-action"
+        class="action-button ticket-task-action"
         data-url="tickets.php?id=<?php echo $ticket->getId(); ?>#tasks"
         data-dialog='{"size":"large"}'
         href="#tickets/<?php
-            echo $ticket->getId(); ?>/add-task"> <?php
+            echo $ticket->getId(); ?>/add-task">
+            <i class="icon-plus-sign"></i> <?php
             print __('Add New Task'); ?></a>
     <?php
-    } ?>
+    }
+    Task::getAgentActions($thisstaff, array('morelabel' => __('Options')));
+    ?>
 </div>
 <br/>
 <div>
 <?php
 if ($count) { ?>
-<form action="#tickets/<?php echo $ticket->getId(); ?>/tasks" method="POST" name='tasks' style="padding-top:10px;">
+<form action="#tickets/<?php echo $ticket->getId(); ?>/tasks" method="POST"
+    name='tasks' id="tasks" style="padding-top:7px;">
 <?php csrf_token(); ?>
  <input type="hidden" name="a" value="mass_process" >
  <input type="hidden" name="do" id="action" value="" >
@@ -49,8 +56,7 @@ if ($count) { ?>
     <thead>
         <tr>
             <?php
-            //TODO: support mass actions.
-            if (0) {?>
+            if (1) {?>
             <th width="8px">&nbsp;</th>
             <?php
             } ?>
@@ -76,21 +82,20 @@ if ($count) { ?>
         $title = Format::htmlchars(Format::truncate($task->getTitle(),40));
         $threadcount = $task->getThread() ?
             $task->getThread()->getNumEntries() : 0;
+
+        $viewhref = sprintf('#tickets/%d/tasks/%d/view',
+                $ticket->getId(), $id);
+
         ?>
         <tr id="<?php echo $id; ?>">
-            <?php
-            //Implement mass  action....if need be.
-            if (0) { ?>
             <td align="center" class="nohover">
                 <input class="ckb" type="checkbox" name="tids[]"
                 value="<?php echo $id; ?>" <?php echo $sel?'checked="checked"':''; ?>>
             </td>
-            <?php
-            } ?>
             <td align="center" nowrap>
               <a class="Icon no-pjax preview"
                 title="<?php echo __('Preview Task'); ?>"
-                href="#tasks/<?php echo $id; ?>/view"
+                href="<?php echo $viewhref; ?>"
                 data-preview="#tasks/<?php echo $id; ?>/preview"
                 ><?php echo $task->getNumber(); ?></a></td>
             <td align="center" nowrap><?php echo
@@ -98,7 +103,7 @@ if ($count) { ?>
             <td><?php echo $status; ?></td>
             <td><a <?php if ($flag) { ?> class="no-pjax"
                     title="<?php echo ucfirst($flag); ?> Task" <?php } ?>
-                    href="#tasks/<?php echo $id; ?>/view"><?php
+                    href="<?php echo $viewhref; ?>"><?php
                 echo $title; ?></a>
                  <?php
                     if ($threadcount>1)
@@ -137,8 +142,9 @@ $(function() {
         }).show();
         return false;
      });
-    $(document).off('.task-action');
-    $(document).on('click.task-action', 'a.task-action', function(e) {
+    // Ticket Tasks
+    $(document).off('.ticket-task-action');
+    $(document).on('click.ticket-task-action', 'a.ticket-task-action', function(e) {
         e.preventDefault();
         var url = 'ajax.php/'
         +$(this).attr('href').substr(1)
diff --git a/include/staff/ticket-view.inc.php b/include/staff/ticket-view.inc.php
index a0e843a0c47e46144ccc66caaf8911ddd758519f..9bc13c7aa0105d686938be9e9dba8dcfe44093c6 100644
--- a/include/staff/ticket-view.inc.php
+++ b/include/staff/ticket-view.inc.php
@@ -396,102 +396,8 @@ $tcount = $ticket->getThreadEntries($types)->count();
 <div id="ticket_tabs_container">
     <div id="ticket_thread" data-thread-id="<?php echo $ticket->getThread()->getId(); ?>" class="tab_content">
     <?php
-    $threadTypes=array('M'=>'message','R'=>'response', 'N'=>'note');
-    /* -------- Messages & Responses & Notes (if inline)-------------*/
-    $types = array('M', 'R', 'N');
-    if(($thread=$ticket->getThreadEntries($types))) {
-        foreach($thread as $entry) { ?>
-        <table class="thread-entry <?php echo $threadTypes[$entry->type]; ?>" cellspacing="0" cellpadding="1" width="940" border="0">
-            <tr>
-                <th colspan="4" width="100%">
-                <div>
-                    <span class="pull-left">
-                    <span style="display:inline-block"><?php
-                        echo Format::datetime($entry->created);?></span>
-                    <span style="display:inline-block;padding:0 1em;max-width: 500px" class="faded title truncate"><?php
-                        echo $entry->title; ?></span>
-                    </span>
-                <div class="pull-right">
-<?php           if ($entry->hasActions()) {
-                    $actions = $entry->getActions(); ?>
-                    <span class="action-button pull-right" data-dropdown="#entry-action-more-<?php echo $entry->getId(); ?>">
-                        <i class="icon-caret-down"></i>
-                        <span ><i class="icon-cog"></i></span>
-                    </span>
-                    <div id="entry-action-more-<?php echo $entry->getId(); ?>" class="action-dropdown anchor-right">
-                <ul class="title">
-<?php               foreach ($actions as $group => $list) {
-                        foreach ($list as $id => $action) { ?>
-                    <li>
-                    <a class="no-pjax <?php
-                        if (!$action->isEnabled())
-                            echo 'disabled';
-                    ?>" href="#" onclick="javascript:
-                        if ($(this).hasClass('disabled')) return false;
-                        <?php echo str_replace('"', '\\"', $action->getJsStub()); ?>; return false;">
-                        <i class="icon-fixed-width <?php echo $action->getIcon(); ?>"></i> <?php
-                            echo $action->getName();
-                ?></a></li>
-<?php                   }
-                    } ?>
-                </ul>
-                    </div>
-<?php           } ?>
-                    <span style="vertical-align:middle">
-                        <span style="vertical-align:middle;" class="textra">
-        <?php if ($entry->flags & ThreadEntry::FLAG_EDITED) { ?>
-                <span class="label label-bare" title="<?php
-        echo sprintf(__('Edited on %s by %s'), Format::datetime($entry->updated), 'You');
-                ?>"><?php echo __('Edited'); ?></span>
-        <?php } ?>
-                        </span>
-                        <span style="vertical-align:middle;"
-                            class="tmeta faded title"><?php
-                            echo Format::htmlchars($entry->getName()); ?></span>
-                    </span>
-                </div>
-                </th>
-            </tr>
-            <tr><td colspan="4" class="thread-body" id="thread-id-<?php
-                echo $entry->getId(); ?>"><div><?php
-                echo $entry->getBody()->toHtml(); ?></div></td></tr>
-            <?php
-            $urls = null;
-            if ($entry->has_attachments
-                && ($urls = $entry->getAttachmentUrls())) { ?>
-            <tr>
-                <td class="info" colspan="4"><?php
-                    foreach ($entry->attachments as $A) {
-                        if ($A->inline) continue;
-                        $size = '';
-                        if ($A->file->size)
-                            $size = sprintf('<em>(%s)</em>',
-                                Format::file_size($A->file->size));
-?>
-                <a class="Icon file no-pjax" href="<?php echo $A->file->getDownloadUrl();
-                    ?>" target="_blank"><?php echo Format::htmlchars($A->file->name);
-                ?></a><?php echo $size;?>&nbsp;
-<?php               } ?>
-                </td>
-            </tr> <?php
-            }
-            if ($urls) { ?>
-                <script type="text/javascript">
-                    $('#thread-id-<?php echo $entry->getId(); ?>')
-                        .data('urls', <?php
-                            echo JsonDataEncoder::encode($urls); ?>)
-                        .data('id', <?php echo $entry->getId(); ?>);
-                </script>
-<?php
-            } ?>
-        </table>
-        <?php
-        if ($entry->type == 'M')
-            $msgId = $entry->getId();
-       }
-    } else {
-        echo '<p><em>'.__('No entries have been posted to this ticket.').'</em></p>';
-    }?>
+    $ticket->getThread()->render(array('M', 'R', 'N'));
+    ?>
 <div class="clear" style="padding-bottom:10px;"></div>
 <?php if($errors['err']) { ?>
     <div id="msg_error"><?php echo $errors['err']; ?></div>
diff --git a/include/staff/tickets.inc.php b/include/staff/tickets.inc.php
index 8f6a86ce857528594c9e6ee2c607d2ecdcb5485c..bc10b928410dc25aeace52771f34910f7e5c5a35 100644
--- a/include/staff/tickets.inc.php
+++ b/include/staff/tickets.inc.php
@@ -390,11 +390,10 @@ $_SESSION[':Q:tickets'] = $orig_tickets;
      <tbody>
         <?php
         // Setup Subject field for display
-        $subject_field = TicketForm::objects()->one()->getField('subject');
+        $subject_field = TicketForm::getInstance()->getField('subject');
         $class = "row1";
         $total=0;
         $ids=($errors && $_POST['tids'] && is_array($_POST['tids']))?$_POST['tids']:null;
-        $subject_field = TicketForm::objects()->one()->getField('subject');
         foreach ($tickets as $T) {
             $total += 1;
                 $tag=$T['staff_id']?'assigned':'openticket';
diff --git a/js/redactor-osticket.js b/js/redactor-osticket.js
index de2a898253b7910d6c8f583c3023717df1c0e640..23b1ee76a4aaed7305df84ed4f974294dbe53139 100644
--- a/js/redactor-osticket.js
+++ b/js/redactor-osticket.js
@@ -343,6 +343,10 @@ $(function() {
     $(document).on('pjax:start', cleanupRedactorElements);
 });
 
+$(document).on('focusout.redactor', 'div.redactor_richtext', function (e) {
+    $(this).siblings('textarea').trigger('change');
+});
+
 $(document).ajaxError(function(event, request, settings) {
     if (settings.url.indexOf('ajax.php/draft') != -1
             && settings.type.toUpperCase() == 'POST') {
diff --git a/scp/ajax.php b/scp/ajax.php
index f34bcfb930f912ea70e6b4592324924ec62fd4a7..f6e2f36de5eb6a966eca31a4980cae32ea8406d9 100644
--- a/scp/ajax.php
+++ b/scp/ajax.php
@@ -159,6 +159,8 @@ $dispatcher = patterns('',
         url_post('^status/(?P<state>\w+)$', 'setSelectedTicketsStatus'),
         url_get('^(?P<tid>\d+)/tasks$', 'tasks'),
         url('^(?P<tid>\d+)/add-task$', 'addTask'),
+        url_get('^(?P<tid>\d+)/tasks/(?P<id>\d+)/view$', 'task'),
+        url_post('^(?P<tid>\d+)/tasks/(?P<id>\d+)$', 'task'),
         url_get('^lookup', 'lookup'),
         url('^search', patterns('ajax.search.php:SearchAjaxAPI',
             url_get('^$', 'getAdvancedSearchDialog'),
@@ -182,7 +184,10 @@ $dispatcher = patterns('',
         url_get('^(?P<tid>\d+)/delete', 'delete'),
         url_post('^(?P<tid>\d+)/delete$', 'delete'),
         url_get('^(?P<tid>\d+)/view$', 'task'),
-        url_post('^(?P<tid>\d+)$', 'task')
+        url_post('^(?P<tid>\d+)$', 'task'),
+        url('^add$', 'add'),
+        url_get('^mass/(?P<action>[\w.]+)', 'massProcess'),
+        url_post('^mass/(?P<action>[\w.]+)', 'massProcess')
     )),
     url('^/collaborators/', patterns('ajax.tickets.php:TicketsAjaxAPI',
         url_get('^(?P<cid>\d+)/view$', 'viewCollaborator'),
diff --git a/scp/canned.php b/scp/canned.php
index d35ec00c6b58aa207b6cb68d1f6240f64d79764a..34edc39a069b8a2524de622f555b52b28d127d15 100644
--- a/scp/canned.php
+++ b/scp/canned.php
@@ -30,7 +30,7 @@ $canned=null;
 if($_REQUEST['id'] && !($canned=Canned::lookup($_REQUEST['id'])))
     $errors['err']=sprintf(__('%s: Unknown or invalid ID.'), __('canned response'));
 
-$canned_form = new Form(array(
+$canned_form = new SimpleForm(array(
     'attachments' => new FileUploadField(array('id'=>'attach',
         'configuration'=>array('extensions'=>false,
             'size'=>$cfg->getMaxFileSize())
diff --git a/scp/css/scp.css b/scp/css/scp.css
index 279fb557b963772535531845d7744cf91e912bcb..0f7077ae1be1c33108f2ef77d6fd6f20283a7721 100644
--- a/scp/css/scp.css
+++ b/scp/css/scp.css
@@ -2177,6 +2177,11 @@ td.indented {
 #dynamic-actions > tr > td {
     padding: 5px;
 }
+
 .no-margin {
     margin: 0 !important;
 }
+
+.form-simple select, .form-simple input, .form-simple textarea {
+    margin-left: 0;
+}
diff --git a/scp/faq.php b/scp/faq.php
index 03c5f160118ba06081f1e53b33afc867a84d9b10..09b0d6bcb04a0b1cb0c9cfca40786b4c7b26625d 100644
--- a/scp/faq.php
+++ b/scp/faq.php
@@ -46,7 +46,7 @@ if ($langs = $cfg->getSecondaryLanguages()) {
     }
 }
 
-$faq_form = new Form($form_fields, $_POST);
+$faq_form = new SimpleForm($form_fields, $_POST);
 
 if ($_POST) {
     $errors=array();
diff --git a/scp/js/scp.js b/scp/js/scp.js
index aed3618a2be9c6bb4a99301f5e38303cafca4821..d3026828db81d44cd77c82bb6bfbe3623d59c058 100644
--- a/scp/js/scp.js
+++ b/scp/js/scp.js
@@ -326,7 +326,9 @@ var scp_prep = function() {
 
     $('.dialog').delegate('input.close, a.close', 'click', function(e) {
         e.preventDefault();
-        $(this).parents('div.dialog')
+        var $dialog = $(this).parents('div.dialog');
+        $dialog.off('blur.redactor');
+        $dialog
         .hide()
         .removeAttr('style');
         $.toggleOverlay(false);
diff --git a/scp/settings.php b/scp/settings.php
index 2fef525750d4ac1db73ccbdf6e576129f5ca5435..a0d0a8c2a8ddba53f215a076a1d1acdb29dfd8e8 100644
--- a/scp/settings.php
+++ b/scp/settings.php
@@ -21,6 +21,8 @@ $settingOptions=array(
         array(__('System Settings'), 'settings.system'),
     'tickets' =>
         array(__('Ticket Settings and Options'), 'settings.ticket'),
+    'tasks' =>
+        array(__('Task Settings and Options'), 'settings.tasks'),
     'pages' =>
         array(__('Site Pages'), 'settings.pages'),
     'access' =>
diff --git a/scp/tasks.php b/scp/tasks.php
new file mode 100644
index 0000000000000000000000000000000000000000..ab0996e8822a3370f33b8b8e8d64cb62ba297d9c
--- /dev/null
+++ b/scp/tasks.php
@@ -0,0 +1,201 @@
+<?php
+/*************************************************************************
+    tasks.php
+
+    Copyright (c)  2006-2013 osTicket
+    http://www.osticket.com
+
+    Released under the GNU General Public License WITHOUT ANY WARRANTY.
+    See LICENSE.TXT for details.
+
+    vim: expandtab sw=4 ts=4 sts=4:
+**********************************************************************/
+
+require('staff.inc.php');
+require_once(INCLUDE_DIR.'class.task.php');
+
+$page = '';
+$task = null; //clean start.
+if ($_REQUEST['id']) {
+    if (!($task=Task::lookup($_REQUEST['id'])))
+         $errors['err'] = sprintf(__('%s: Unknown or invalid ID.'), __('task'));
+    elseif (!$task->checkStaffPerm($thisstaff)) {
+        $errors['err'] = __('Access denied. Contact admin if you believe this is in error');
+        $task = null;
+    }
+}
+
+// Configure form for file uploads
+$note_form = new SimpleForm(array(
+    'attachments' => new FileUploadField(array('id'=>'attach',
+        'name'=>'attach:note',
+        'configuration' => array('extensions'=>'')))
+));
+
+//At this stage we know the access status. we can process the post.
+if($_POST && !$errors):
+
+    if ($task) {
+        //More coffee please.
+        $errors=array();
+        $role = $thisstaff->getRole($task->getDeptId());
+        switch(strtolower($_POST['a'])):
+        case 'postnote': /* Post Internal Note */
+            $vars = $_POST;
+            $attachments = $note_form->getField('attachments')->getClean();
+            $vars['cannedattachments'] = array_merge(
+                $vars['cannedattachments'] ?: array(), $attachments);
+
+            $wasOpen = ($task->isOpen());
+            if(($note=$task->postNote($vars, $errors, $thisstaff))) {
+
+                $msg=__('Internal note posted successfully');
+                // Clear attachment list
+                $note_form->setSource(array());
+                $note_form->getField('attachments')->reset();
+
+                if($wasOpen && $task->isClosed())
+                    $task = null; //Going back to main listing.
+                else
+                    // Ticket is still open -- clear draft for the note
+                    Draft::deleteForNamespace('task.note.'.$task->getId(),
+                        $thisstaff->getId());
+
+            } else {
+
+                if(!$errors['err'])
+                    $errors['err'] = __('Unable to post internal note - missing or invalid data.');
+
+                $errors['postnote'] = __('Unable to post the note. Correct the error(s) below and try again!');
+            }
+            break;
+        default:
+            $errors['err']=__('Unknown action');
+        endswitch;
+    }
+    if(!$errors)
+        $thisstaff->resetStats(); //We'll need to reflect any changes just made!
+endif;
+
+/*... Quick stats ...*/
+$stats= $thisstaff->getTasksStats();
+
+// Clear advanced search upon request
+if (isset($_GET['clear_filter']))
+    unset($_SESSION['advsearch']);
+
+//Navigation
+$nav->setTabActive('tasks');
+$open_name = _P('queue-name',
+    /* This is the name of the open tasks queue */
+    'Open');
+
+$nav->addSubMenu(array('desc'=>$open_name.' ('.number_format($stats['open']).')',
+                       'title'=>__('Open Tasks'),
+                       'href'=>'tasks.php?status=open',
+                       'iconclass'=>'Ticket'),
+                    ((!$_REQUEST['status'] && !isset($_SESSION['advsearch'])) || $_REQUEST['status']=='open'));
+
+if ($stats['assigned']) {
+
+    $nav->addSubMenu(array('desc'=>__('My Tasks').' ('.number_format($stats['assigned']).')',
+                           'title'=>__('Assigned Tasks'),
+                           'href'=>'tasks.php?status=assigned',
+                           'iconclass'=>'assignedTickets'),
+                        ($_REQUEST['status']=='assigned'));
+}
+
+if ($stats['overdue']) {
+    $nav->addSubMenu(array('desc'=>__('Overdue').' ('.number_format($stats['overdue']).')',
+                           'title'=>__('Stale Tasks'),
+                           'href'=>'tasks.php?status=overdue',
+                           'iconclass'=>'overdueTickets'),
+                        ($_REQUEST['status']=='overdue'));
+
+    if(!$sysnotice && $stats['overdue']>10)
+        $sysnotice=sprintf(__('%d overdue tasks!'), $stats['overdue']);
+}
+
+if ($stats['closed']) {
+    $nav->addSubMenu(array('desc' => __('Completed').' ('.number_format($stats['closed']).')',
+                           'title'=>__('Completed Tasks'),
+                           'href'=>'tasks.php?status=closed',
+                           'iconclass'=>'closedTickets'),
+                        ($_REQUEST['status']=='closed'));
+}
+
+if (isset($_SESSION['advsearch'])) {
+    // XXX: De-duplicate and simplify this code
+    $search = SavedSearch::create();
+    $form = $search->getFormFromSession('advsearch');
+    $form->loadState($_SESSION['advsearch']);
+    $tasks = Task::objects();
+    $tasks = $search->mangleQuerySet($tasks, $form);
+    $count = $tasks->count();
+    $nav->addSubMenu(array('desc' => __('Search').' ('.number_format($count).')',
+                           'title'=>__('Advanced Task Search'),
+                           'href'=>'tasks.php?status=search',
+                           'iconclass'=>'Ticket'),
+                        (!$_REQUEST['status'] || $_REQUEST['status']=='search'));
+}
+
+if ($thisstaff->hasPerm(TaskModel::PERM_CREATE)) {
+    $nav->addSubMenu(array('desc'=>__('New Task'),
+                           'title'=> __('Open a New Task'),
+                           'href'=>'#tasks/add',
+                           'iconclass'=>'newTicket task-action',
+                           'id' => 'new-task',
+                           'attr' => array(
+                               'data-dialog' => '{"size":"large"}'
+                               )
+                           ),
+                        ($_REQUEST['a']=='open'));
+}
+
+
+$ost->addExtraHeader('<script type="text/javascript" src="js/ticket.js"></script>');
+$ost->addExtraHeader('<meta name="tip-namespace" content="tasks.queue" />',
+    "$('#content').data('tipNamespace', 'tasks.queue');");
+
+if($task) {
+    $ost->setPageTitle(sprintf(__('Task #%s'),$task->getNumber()));
+    $nav->setActiveSubMenu(-1);
+    $inc = 'task-view.inc.php';
+    if ($_REQUEST['a']=='edit'
+            && $task->checkStaffPerm($thisstaff, TaskModel::PERM_EDIT)) {
+        $inc = 'task-edit.inc.php';
+        if (!$forms) $forms=DynamicFormEntry::forObject($task->getId(), 'A');
+        // Auto add new fields to the entries
+        foreach ($forms as $f) $f->addMissingFields();
+    } elseif($_REQUEST['a'] == 'print' && !$task->pdfExport($_REQUEST['psize']))
+        $errors['err'] = __('Internal error: Unable to export the task to PDF for print.');
+} else {
+	$inc = 'tasks.inc.php';
+    if ($_REQUEST['a']=='open' &&
+            $thisstaff->hasPerm(Task::PERM_CREATE))
+        $inc = 'task-open.inc.php';
+    elseif($_REQUEST['a'] == 'export') {
+        $ts = strftime('%Y%m%d');
+        if (!($query=$_SESSION[':Q:tasks']))
+            $errors['err'] = __('Query token not found');
+        elseif (!Export::saveTasks($query, "tasks-$ts.csv", 'csv'))
+            $errors['err'] = __('Internal error: Unable to dump query results');
+    }
+
+    //Clear active submenu on search with no status
+    if($_REQUEST['a']=='search' && !$_REQUEST['status'])
+        $nav->setActiveSubMenu(-1);
+
+    //set refresh rate if the user has it configured
+    if(!$_POST && !$_REQUEST['a'] && ($min=$thisstaff->getRefreshRate())) {
+        $js = "clearTimeout(window.task_refresh);
+               window.task_refresh = setTimeout($.refreshTaskView,"
+            .($min*60000).");";
+        $ost->addExtraHeader('<script type="text/javascript">'.$js.'</script>',
+            $js);
+    }
+}
+
+require_once(STAFFINC_DIR.'header.inc.php');
+require_once(STAFFINC_DIR.$inc);
+require_once(STAFFINC_DIR.'footer.inc.php');
diff --git a/scp/tickets.php b/scp/tickets.php
index 4d15d30dee7ce88608d9ab9991436433321fd126..c4a9591e25049e1a9c7292d089ce5a8c2a695ee4 100644
--- a/scp/tickets.php
+++ b/scp/tickets.php
@@ -40,12 +40,12 @@ if ($_REQUEST['uid'])
     $user = User::lookup($_REQUEST['uid']);
 
 // Configure form for file uploads
-$response_form = new Form(array(
+$response_form = new SimpleForm(array(
     'attachments' => new FileUploadField(array('id'=>'attach',
         'name'=>'attach:response',
         'configuration' => array('extensions'=>'')))
 ));
-$note_form = new Form(array(
+$note_form = new SimpleForm(array(
     'attachments' => new FileUploadField(array('id'=>'attach',
         'name'=>'attach:note',
         'configuration' => array('extensions'=>'')))
diff --git a/setup/doc/forms.md b/setup/doc/forms.md
index c3545096c5e6daaee3ff394eb4b490f9a5a84daa..f1520cd3712cb8093fbb83787027bc15db3b051c 100644
--- a/setup/doc/forms.md
+++ b/setup/doc/forms.md
@@ -16,7 +16,7 @@ constructor.
 The simplest way to create forms is to instanciate the Form instance
 directly:
 
-    $form = new Form(array(
+    $form = new SimpleForm(array(
         'email' => new TextboxField(array('label'=>'Email Address')),
     );
 
@@ -34,7 +34,7 @@ the cleaned values from the form fields based on the data from the request.
 To create a class that defines the fields statically, one might write a
 trampoline constructor:
 
-    class UserForm extends Form {
+    class UserForm extends SimpleForm {
         function __construct() {
             $args = func_get_args();
             $fields = array(