diff --git a/include/ajax.tickets.php b/include/ajax.tickets.php
index e4fdafcc25a8a72fa2d0eb0cb721eebafd5feae5..84e939550b93de56efea6f194a7ef2a3a07e2c7c 100644
--- a/include/ajax.tickets.php
+++ b/include/ajax.tickets.php
@@ -695,5 +695,256 @@ class TicketsAjaxAPI extends AjaxController {
 
         return $canned->getFormattedResponse($format, $varReplacer);
     }
+
+    function changeTicketStatus($tid, $status) {
+        global $thisstaff;
+
+        if (!$thisstaff)
+            Http::response(403, 'Access denied');
+        elseif (!$tid
+                || !($ticket=Ticket::lookup($tid))
+                || !$ticket->checkStaffAccess($thisstaff))
+            Http::response(404, 'Unknown ticket #');
+
+        $info = array();
+        switch($status) {
+            case 'open':
+            case 'reopen':
+                $state = 'open';
+                break;
+            case 'resolve':
+                $state = 'resolved';
+                break;
+            case 'close':
+                if (!$thisstaff->canCloseTickets())
+                    Http::response(403, 'Access denied');
+                $state = 'closed';
+                break;
+            case 'delete':
+                if (!$thisstaff->canDeleteTickets())
+                    Http::response(403, 'Access denied');
+                $state = 'deleted';
+                $info = array(
+                        'warn'  => sprintf(__('Are you sure you want to DELETE %s?'),
+                            __('this ticket')),
+                        //TODO: remove message below once we ship data retention plug
+                        'extra' => sprintf('<strong>%s</strong>',
+                            __('Deleted tickets CANNOT be recovered,
+                                including any associated attachments.')));
+                break;
+            default:
+                $info['warn'] = sprintf('%s %s',
+                        __('Unknown or invalid'), __('status'));
+        }
+
+        $verb = TicketStateField::getVerb($state);
+
+        $info['action'] = sprintf('#tickets/%d/status/%s', $ticket->getId(), $status);
+        $info['title'] = sprintf('%s %s #%s',
+                $verb ?: $status,
+                __('Ticket'), $ticket->getNumber());
+        $info['status_id'] = $_REQUEST['status_id'] ?: $ticket->getStatusId();
+
+        return self::_setStatus($state, $info);
+    }
+
+    function setTicketStatus($tid, $state) {
+        global $thisstaff, $ost;
+
+        if (!$thisstaff)
+            Http::response(403, 'Access denied');
+        elseif (!$tid
+                || !($ticket=Ticket::lookup($tid))
+                || !$ticket->checkStaffAccess($thisstaff))
+            Http::response(404, 'Unknown ticket #');
+
+        $errors = $info = array();
+        if (!($status= TicketStatus::lookup($_REQUEST['status_id'])))
+            $errors['status_id'] = sprintf('%s %s',
+                    __('Unknown or invalid'), __('status'));
+        elseif (!$errors) {
+            // Make sure the agent has permission to set the status
+            switch(mb_strtolower($status->getState())) {
+                case 'open':
+                    if (!$thisstaff->canCloseTickets()
+                            && !$thisstaff->canCreateTickets())
+                        $errors['err'] = sprintf(__('You do not have
+                                    permission to %s.'),
+                                __('reopen tickets'));
+                    break;
+                case 'resolved':
+                case 'closed':
+                    if (!$thisstaff->canCloseTickets())
+                        $errors['err'] = sprintf(__('You do not have
+                                    permission to %s.'),
+                                __('resolve/close tickets'));
+                    break;
+                case 'deleted':
+                    if (!$thisstaff->canDeleteTickets())
+                        $errors['err'] = sprintf(__('You do not have
+                                    permission to %s.'),
+                                __('archive/delete tickets'));
+                    break;
+                default:
+                    $errors['err'] = sprintf('%s %s',
+                            __('Unknown or invalid'), __('status'));
+            }
+        }
+
+        if ($ticket->setStatus($status, $_REQUEST['comments'])) {
+            $_SESSION['::sysmsgs']['msg'] = sprintf(
+                    __('Successfully updated status to %s'),
+                    $status->getName());
+            Http::response(201, 'Successfully processed');
+        }
+
+        $errors['err'] = __('Error updating ticket status');
+        $info['errors'] = $errors;
+        return self::_setStatus($state, $info);
+    }
+
+    function changeTicketsStatus($status, $id=0) {
+        global $thisstaff, $cfg;
+
+        if (!$thisstaff)
+            Http::response(403, 'Access denied');
+
+        $state = null;
+        $info = array();
+        switch($status) {
+            case 'open':
+            case 'reopen':
+                $state = 'open';
+                break;
+            case 'resolve':
+                $state = 'resolved';
+                break;
+            case 'close':
+                if (!$thisstaff->canCloseTickets())
+                    Http::response(403, 'Access denied');
+                $state = 'closed';
+                break;
+            case 'delete':
+                if (!$thisstaff->canDeleteTickets())
+                    Http::response(403, 'Access denied');
+
+                $state = 'deleted';
+                $info = array(
+                        'warn'  => sprintf(__('Are you sure you want to DELETE %s?'),
+                            _N('selected ticket', 'selected tickets', $_REQUEST['count'])),
+                        //TODO: remove message below once we ship data retention plug
+                        'extra' => sprintf('<strong>%s</strong>',
+                            __('Deleted tickets CANNOT be recovered,
+                                including any associated attachments.')));
+                break;
+            default:
+                $info['warn'] = sprintf('%s %s',
+                        __('Unknown or invalid'), __('status'));
+        }
+
+        $info['title'] = sprintf('%s %s',
+                TicketStateField::getVerb($state),
+                 __('Tickets'));
+
+        if ($_REQUEST['count'])
+            $info['title'] .= sprintf(' &mdash; %d %s',
+                    $_REQUEST['count'], __('selected'));
+
+        $info['status_id'] = $id;
+
+        return self::_setStatus($state, $info);
+    }
+
+    function setTicketsStatus($state) {
+        global $thisstaff, $ost;
+
+        $errors = $info = array();
+        if (!$thisstaff || !$thisstaff->canManageTickets())
+            $errors['err']=__('You do not have permission to mass manage tickets. Contact admin for such access');
+        elseif (!$_REQUEST['tids'] || !count($_REQUEST['tids']))
+            $errors['err']=sprintf(__('You must select at least %s.'),
+                    __('one ticket'));
+        elseif (!($status= TicketStatus::lookup($_REQUEST['status_id'])))
+            $errors['status_id'] = sprintf('%s %s',
+                    __('Unknown or invalid'), __('status'));
+        elseif (!$errors) {
+            // Make sure the agent has permission to set the status
+            switch(mb_strtolower($status->getState())) {
+                case 'open':
+                    if (!$thisstaff->canCloseTickets()
+                            && !$thisstaff->canCreateTickets())
+                        $errors['err'] = sprintf(__('You do not have
+                                    permission to %s.'),
+                                __('reopen tickets'));
+                    break;
+                case 'resolved':
+                case 'closed':
+                    if (!$thisstaff->canCloseTickets())
+                        $errors['err'] = sprintf(__('You do not have
+                                    permission to %s.'),
+                                __('resolve/close tickets'));
+                    break;
+                case 'deleted':
+                    if (!$thisstaff->canDeleteTickets())
+                        $errors['err'] = sprintf(__('You do not have
+                                    permission to %s.'),
+                                __('archive/delete tickets'));
+                    break;
+                default:
+                    $errors['err'] = sprintf('%s %s',
+                            __('Unknown or invalid'), __('status'));
+            }
+        }
+
+        if (!$errors) {
+            $i = 0;
+            $count = count($_REQUEST['tids']);
+            $comments = $_REQUEST['comments'];
+            foreach ($_REQUEST['tids'] as $tid) {
+                if (($ticket=Ticket::lookup($tid))
+                        && $ticket->getStatusId() != $status->getId()
+                        && $ticket->checkStaffAccess($thisstaff)
+                        && $ticket->setStatus($status, $comments))
+                    $i++;
+            }
+
+            if (!$i)
+                $errors['err'] = sprintf(__('Unable to set status for %s'),
+                        _N('selected ticket', 'selected tickets', $count));
+            else {
+                // Assume success
+                if ($i==$count) {
+                    $_SESSION['::sysmsgs']['msg'] = sprintf(
+                            __('Successfully updated %s status to %s'),
+                            _N('selected ticket', 'selected tickets',
+                                $count),
+                            $status->getName());
+                } else {
+                    $_SESSION['::sysmsgs']['warn'] = sprintf(
+                            __('%1$d of %2$d %3$s status updated to %4$s'),$i, $count,
+                            _N('selected ticket', 'selected tickets',
+                                $count),
+                            $status->getName());
+                }
+
+                Http::response(201, 'Successfully processed');
+            }
+        }
+
+        $info['errors'] = $errors;
+        return self::_setStatus($state, $info);
+    }
+
+    function _setStatus($state, $info=array()) {
+
+        $errors = array();
+        if ($info && isset($info['errors']))
+            $errors = $info['errors'];
+
+        if (!$info['error'] && isset($errors['err']))
+            $info['error'] = $errors['err'];
+
+        include(STAFFINC_DIR . 'templates/ticket-status.tmpl.php');
+    }
 }
 ?>
diff --git a/include/class.forms.php b/include/class.forms.php
index 4411290f073dbde95d745d146ae683c8680e210c..c5c93866e9fa35b9404de09a3cf54dc2fe095628 100644
--- a/include/class.forms.php
+++ b/include/class.forms.php
@@ -787,6 +787,10 @@ class ChoiceField extends FormField {
     }
 
     function toString($value) {
+        return (string) $this->getChoice($value);
+    }
+
+    function getChoice($value) {
 
         $choices = $this->getChoices();
         $selection = array();
@@ -1021,12 +1025,30 @@ FormField::addFieldTypes(/*trans*/ 'Dynamic Fields', function() {
 
 class TicketStateField extends ChoiceField {
 
-    static $_choices = array(
-            'open' => 'Open',
-            'resolved' => 'Resolved',
-            'closed' => 'Closed',
-            'archived' => 'Archived',
-            'deleted' => 'Deleted'
+    static $_states = array(
+            'open' => array(
+                'name' => /* trans */ 'Open',
+                'verb' => /* trans */ 'Open'
+                ),
+            'resolved' => array(
+                'name' => /* trans */ 'Resolved',
+                'verb' => /* trans */ 'Resolve'
+                ),
+            'closed' => array(
+                'name' => /* trans */ 'Closed',
+                'verb' => /* trans */ 'Close'
+                )
+            );
+    // Private states
+    static $_privatestates = array(
+            'archived' => array(
+                'name' => /* trans */ 'Archived',
+                'verb' => /* trans */ 'Archive'
+                ),
+            'deleted'  => array(
+                'name' => /* trans */ 'Deleted',
+                'verb' => /* trans */ 'Delete'
+                )
             );
 
     function hasIdValue() {
@@ -1038,20 +1060,51 @@ class TicketStateField extends ChoiceField {
     }
 
     function getChoices() {
-        $this->ht['default'] =  '';
+        static $_choices;
+
+        if (!isset($_choices)) {
+            // Translate and cache the choices
+            foreach (static::$_states as $k => $v)
+                $_choices[$k] =  __($v['name']);
+
+            $this->ht['default'] =  '';
+        }
 
-        return static::$_choices;
+        return $_choices;
+    }
+
+    function getChoice($state) {
+
+        if ($state && is_array($state))
+            $state = key($state);
+
+        if (isset(static::$_states[$state]))
+            return __(static::$_states[$state]['name']);
+
+        if (isset(static::$_privatestates[$state]))
+            return __(static::$_privatestates[$state]['name']);
+
+        return $state;
     }
 
     function getConfigurationOptions() {
         return array(
             'prompt' => new TextboxField(array(
-                'id'=>2, 'label'=>'Prompt', 'required'=>false, 'default'=>'',
-                'hint'=>'Leading text shown before a value is selected',
+                'id'=>2, 'label'=> __('Prompt'), 'required'=>false, 'default'=>'',
+                'hint'=> __('Leading text shown before a value is selected'),
                 'configuration'=>array('size'=>40, 'length'=>40),
             )),
         );
     }
+
+    static function getVerb($state) {
+
+        if (isset(static::$_states[$state]))
+            return __(static::$_states[$state]['verb']);
+
+        if (isset(static::$_privatestates[$state]))
+            return __(static::$_privatestates[$state]['verb']);
+    }
 }
 FormField::addFieldTypes('Dynamic Fields', function() {
     return array(
diff --git a/include/class.list.php b/include/class.list.php
index 6f322d35e88b982346f4a28d187abbcaeec27b47..31a3dd51938e8f56fd841021b7021d72a1ec3291 100644
--- a/include/class.list.php
+++ b/include/class.list.php
@@ -649,30 +649,38 @@ class TicketStatusList extends CustomListHandler {
         return TicketStatus::objects()->count();
     }
 
-    function getAllItems($states=array()) {
-        if ($states)
-            $items = TicketStatus::objects()->filter(
-                    array('state__in' => $states))
-                    ->order_by($this->getListOrderBy());
-        else
-            $items = TicketStatus::objects()->order_by($this->getListOrderBy());
+    function getAllItems() {
+        if (!$this->_items)
+            $this->_items = TicketStatus::objects()->order_by($this->getListOrderBy());
 
-        return $items;
+        return $this->_items;
     }
 
-    function getItems($criteria) {
+    function getItems($criteria = array()) {
 
-        if (!$this->_items) {
-            $this->_items = TicketStatus::objects()->filter(
-                array('flags__hasbit' => TicketStatus::ENABLED))
-                ->order_by($this->getListOrderBy());
-            if ($criteria['limit'])
-                $this->_items->limit($criteria['limit']);
-            if ($criteria['offset'])
-                $this->_items->offset($criteria['offset']);
-        }
+        // Default to only enabled items
+        if (!isset($criteria['enabled']))
+            $criteria['enabled'] = true;
 
-        return $this->_items;
+        $filters =  array();
+        if ($criteria['enabled'])
+            $filters['mode__hasbit'] = TicketStatus::ENABLED;
+        if ($criteria['states'] && is_array($criteria['states']))
+            $filters['state__in'] = $criteria['states'];
+        else
+            $filters['state__isnull'] = false;
+
+        $items = TicketStatus::objects();
+        if ($filters)
+            $items->filter($filters);
+        if ($criteria['limit'])
+            $items->limit($criteria['limit']);
+        if ($criteria['offset'])
+            $items->offset($criteria['offset']);
+
+        $items->order_by($this->getListOrderBy());
+
+        return $items;
     }
 
     function getItem($val) {
@@ -686,6 +694,7 @@ class TicketStatusList extends CustomListHandler {
     function addItem($vars, &$errors) {
 
         $item = TicketStatus::create(array(
+            'mode' => 1,
             'flags' => 0,
             'sort'  => $vars['sort'],
             'name' => $vars['value'],
@@ -697,12 +706,12 @@ class TicketStatusList extends CustomListHandler {
         return $item;
     }
 
-    static function getAll($states=array()) {
+    static function getStatuses($criteria=array()) {
 
         $statuses = array();
         if (($list = DynamicList::lookup(
                         array('type' => 'ticket-status'))))
-            $statuses = $list->getAllItems($states);
+            $statuses = $list->getItems($criteria);
 
         return $statuses;
     }
@@ -739,12 +748,18 @@ class TicketStatus  extends VerySimpleModel implements CustomListItem {
         'table' => TICKET_STATUS_TABLE,
         'ordering' => array('name'),
         'pk' => array('id'),
+        'joins' => array(
+            'tickets' => array(
+                'reverse' => 'TicketModel.status',
+                )
+        )
     );
 
     var $_list;
     var $_form;
     var $_config;
     var $_settings;
+    var $_properties;
 
     const ENABLED   = 0x0001;
     const INTERNAL  = 0x0002; // Forbid deletion or name and status change.
@@ -813,7 +828,7 @@ class TicketStatus  extends VerySimpleModel implements CustomListItem {
     }
 
     function isEnableable() {
-        return $this->hasProperties();
+        return ($this->getState());
     }
 
     function isDisableable() {
@@ -822,11 +837,19 @@ class TicketStatus  extends VerySimpleModel implements CustomListItem {
 
     function isDeletable() {
 
-        return !($this->isInternal() || $this->isDefault());
+        return !($this->isInternal()
+                || $this->isDefault()
+                || $this->getNumTickets());
     }
 
     function isInternal() {
-        return ($this->hasFlag('mode', self::INTERNAL));
+        return ($this->isDefault()
+                || $this->hasFlag('mode', self::INTERNAL));
+    }
+
+
+    function getNumTickets() {
+        return $this->tickets->count();
     }
 
     function getId() {
@@ -853,14 +876,25 @@ class TicketStatus  extends VerySimpleModel implements CustomListItem {
         return $this->get('sort');
     }
 
+    private function getProperties() {
+
+        if (!isset($this->_properties)) {
+            $this->_properties = $this->_config->get('properties');
+            if (is_string($this->_properties))
+                $this->_properties = JsonDataParser::parse($this->_properties);
+            elseif (!$this->_properties)
+                $this->_properties = array();
+        }
+
+        return $this->_properties;
+    }
+
     function getConfiguration() {
 
         if (!$this->_settings) {
-             $this->_settings = $this->_config->get('properties');
-             if (is_string($this->_settings))
-                 $this->_settings = JsonDataParser::parse($this->_settings);
-             elseif (!$this->_settings)
-                 $this->_settings = array();
+            $this->_settings = $this->getProperties();
+            if (!$this->_settings)
+                $this->_settings = array();
 
             if ($this->getConfigurationForm()) {
                 foreach ($this->getConfigurationForm()->getFields() as $f)  {
@@ -952,11 +986,11 @@ class TicketStatus  extends VerySimpleModel implements CustomListItem {
 
     function delete() {
 
+        // Statuses with tickets are not deletable
         if (!$this->isDeletable())
             return false;
 
-        // TODO: Delete and do house cleaning (move tickets..etc)
-
+        return parent::delete();
     }
 
     function toString() {
@@ -967,6 +1001,15 @@ class TicketStatus  extends VerySimpleModel implements CustomListItem {
         return $this->toString();
     }
 
+    static function create($ht) {
+
+        if (!isset($ht['mode']))
+            $ht['mode'] = 1;
+
+        $ht['created'] = new SqlFunction('NOW');
+
+        return  parent::create($ht);
+    }
 
     static function lookup($var, $list= false) {
 
@@ -984,7 +1027,6 @@ class TicketStatus  extends VerySimpleModel implements CustomListItem {
 
         $properties = JsonDataEncoder::encode($ht['properties']);
         unset($ht['properties']);
-        $ht['created'] = new SqlFunction('NOW');
         if ($status = TicketStatus::create($ht)) {
             $status->save(true);
             $status->_config = new Config('TS.'.$status->getId());
@@ -993,9 +1035,11 @@ class TicketStatus  extends VerySimpleModel implements CustomListItem {
 
         return $status;
     }
-}
-
-
 
+    static function options() {
+        include(STAFFINC_DIR . 'templates/status-options.tmpl.php');
+    }
+}
 
+TicketStatus::_inspect();
 ?>
diff --git a/include/class.nav.php b/include/class.nav.php
index 6f72add47f346900b3230096cd5324da0e14a6eb..bc4956f084b4b810b17d242637f38bcc365bbee8 100644
--- a/include/class.nav.php
+++ b/include/class.nav.php
@@ -102,6 +102,8 @@ class StaffNav {
 
     function addSubMenu($item,$active=false){
 
+        // Triger lazy loading if submenus haven't been initialized
+        isset($this->submenus[$this->getPanel().'.'.$this->activetab]);
         $this->submenus[$this->getPanel().'.'.$this->activetab][]=$item;
         if($active)
             $this->activeMenu=sizeof($this->submenus[$this->getPanel().'.'.$this->activetab]);
diff --git a/include/class.orm.php b/include/class.orm.php
index f46fd8ea5a37972e032d89ae52ce8198e46f95f7..264582fb6885c4dd636aaa09f9777f5c421e652d 100644
--- a/include/class.orm.php
+++ b/include/class.orm.php
@@ -42,10 +42,14 @@ class VerySimpleModel {
         elseif (isset(static::$meta['joins'][$field])) {
             // TODO: Support instrumented lists and such
             $j = static::$meta['joins'][$field];
-            $class = $j['fkey'][0];
-            $v = $this->ht[$field] = $class::lookup(
-                array($j['fkey'][1] => $this->ht[$j['local']]));
-            return $v;
+            // Make sure joins were inspected
+            if (isset($j['fkey'])
+                    && ($class = $j['fkey'][0])
+                    && class_exists($class)) {
+                $v = $this->ht[$field] = $class::lookup(
+                    array($j['fkey'][1] => $this->ht[$j['local']]));
+                return $v;
+            }
         }
         if (isset($default))
             return $default;
diff --git a/include/class.search.php b/include/class.search.php
index e34d032248f0faec48cfcf6d83a9bb14319c30ef..1beeb2d5c26fa7b1e3aeba5dc4ed0bb2d29475df 100644
--- a/include/class.search.php
+++ b/include/class.search.php
@@ -382,7 +382,7 @@ class MysqlSearchBackend extends SearchBackend {
     }
 
     static function createSearchTable() {
-        $sql = 'CREATE TABLE '.TABLE_PREFIX.'_search (
+        $sql = 'CREATE TABLE IF NOT EXISTS '.TABLE_PREFIX.'_search (
             `object_type` varchar(8) not null,
             `object_id` int(11) unsigned not null,
             `title` text collate utf8_general_ci,
@@ -406,7 +406,8 @@ class MysqlSearchBackend extends SearchBackend {
                 return true;
 
             // Create the search table automatically
-            $class::createSearchTable();
+            $class::__init();
+
         };
 
         // THREADS ----------------------------------
@@ -558,5 +559,14 @@ class MysqlSearchBackend extends SearchBackend {
 
         return true;
     }
+
+    static function __init() {
+        self::createSearchTable();
+    }
+
 }
+
+Signal::connect('system.install',
+        array('MysqlSearchBackend', '__init'));
+
 MysqlSearchBackend::register();
diff --git a/include/class.ticket.php b/include/class.ticket.php
index 5fff02517bc54e36c27ec9d9b1b9fcf16ab7fda1..f451de786c52c8b59ac520e8e4df7996d4fbb696 100644
--- a/include/class.ticket.php
+++ b/include/class.ticket.php
@@ -828,7 +828,7 @@ class Ticket {
     }
 
     //Status helper.
-    function setStatus($status) {
+    function setStatus($status, $comments='') {
         global $thisstaff;
 
         if ($status && is_numeric($status))
@@ -837,6 +837,10 @@ class Ticket {
         if (!$status || !$status instanceof TicketStatus)
             return false;
 
+        // XXX: intercept deleted status and do hard delete
+        if (!strcasecmp($status->getState(), 'deleted'))
+            return $this->delete($comments);
+
         if ($this->getStatusId() == $status->getId())
             return true;
 
@@ -845,6 +849,7 @@ class Ticket {
 
         $ecb = null;
         switch($status->getState()) {
+            case 'resolved':
             case 'closed':
                 $sql.=', closed=NOW(), duedate=NULL ';
                 if ($thisstaff)
@@ -857,6 +862,7 @@ class Ticket {
                 };
                 break;
             case 'open':
+                // TODO: check current status if it allows for reopening
                 if ($this->isClosed()) {
                     $sql.= ',closed=NULL, reopened=NOW() ';
 
@@ -865,6 +871,9 @@ class Ticket {
                     };
                 }
                 break;
+            default:
+                return false;
+
         }
 
         $sql.=' WHERE ticket_id='.db_input($this->getId());
@@ -873,12 +882,19 @@ class Ticket {
             return false;
 
         // Log status change b4 reload
-        $note= sprintf('Status changed from %s to %s by %s',
+        $note = sprintf(__('Status changed from %s to %s by %s'),
                 $this->getStatus(),
                 $status,
                 $thisstaff ?: 'SYSTEM');
 
-        $this->logNote('Status Changed', $note, $thisstaff, false);
+        $alert = false;
+        if ($comments) {
+            $note .= sprintf('<hr>%s', $comments);
+            // Send out alerts if comments are included
+            $alert = true;
+        }
+
+        $this->logNote(__('Status Changed'), $note, $thisstaff, $alert);
 
         // Log events via callback
         if ($ecb) $ecb($this);
@@ -2013,7 +2029,8 @@ class Ticket {
         exit;
     }
 
-    function delete() {
+    function delete($comments='') {
+        global $ost, $thisstaff;
 
         //delete just orphaned ticket thread & associated attachments.
         // Fetch thread prior to removing ticket entry
@@ -2030,6 +2047,18 @@ class Ticket {
 
         $this->deleteDrafts();
 
+        // Log delete
+        $log = sprintf(__('Ticket #%1$s deleted by %2$s'),
+                $this->getNumber(),
+                $thisstaff ? $thisstaff->getName() : __('SYSTEM'));
+
+        if ($comments)
+            $log .= sprintf('<hr>%s', $comments);
+
+        $ost->logDebug(
+                sprintf( __('Ticket #%s deleted'), $this->getNumber()),
+                $log);
+
         return true;
     }
 
@@ -2827,13 +2856,15 @@ class Ticket {
     function checkOverdue() {
 
         $sql='SELECT ticket_id FROM '.TICKET_TABLE.' T1 '
+            .' INNER JOIN '.TICKET_STATUS_TABLE.' status
+                ON (status.id=T1.status_id AND status.state="open") '
             .' LEFT JOIN '.SLA_TABLE.' T2 ON (T1.sla_id=T2.id AND T2.isactive=1) '
-            .' WHERE status=\'open\' AND isoverdue=0 '
+            .' WHERE isoverdue=0 '
             .' AND ((reopened is NULL AND duedate is NULL AND TIME_TO_SEC(TIMEDIFF(NOW(),T1.created))>=T2.grace_period*3600) '
             .' OR (reopened is NOT NULL AND duedate is NULL AND TIME_TO_SEC(TIMEDIFF(NOW(),reopened))>=T2.grace_period*3600) '
             .' OR (duedate is NOT NULL AND duedate<NOW()) '
             .' ) ORDER BY T1.created LIMIT 50'; //Age upto 50 tickets at a time?
-        //echo $sql;
+
         if(($res=db_query($sql)) && db_num_rows($res)) {
             while(list($id)=db_fetch_row($res)) {
                 if(($ticket=Ticket::lookup($id)) && $ticket->markOverdue())
diff --git a/include/class.user.php b/include/class.user.php
index 127a078d5067d1acf43ff136ec6d0f0d27e804b1..8130551a96b1fb7ceda79bc9c2ebf1607b7caf6c 100644
--- a/include/class.user.php
+++ b/include/class.user.php
@@ -39,6 +39,9 @@ class TicketModel extends VerySimpleModel {
         'joins' => array(
             'user' => array(
                 'constraint' => array('user_id' => 'UserModel.id')
+            ),
+            'status' => array(
+                'constraint' => array('status_id' => 'TicketStatus.id')
             )
         )
     );
diff --git a/include/i18n/en_US/list.yaml b/include/i18n/en_US/list.yaml
index ffe4a09cb759c98c308ad12579caf1515f601fad..613ca4a2f83db4a46048c70069a9569c95791a86 100644
--- a/include/i18n/en_US/list.yaml
+++ b/include/i18n/en_US/list.yaml
@@ -42,15 +42,6 @@
         edit_mask: 63
         configuration:
             prompt: State of a ticket
-      - type: flags # notrans
-        name: flags # notrans
-        label: Flags
-        required: false
-        sort: 2
-        edit_mask: 63
-        configuration:
-            prompt: Ticket Flags
-            multiselect: true
       - type: memo # notrans
         name: description # notrans
         label: Description
@@ -64,68 +55,3 @@
             length: 100
   configuration:
     handler: TicketStatusList
-
-# Ticket flags
-- type: ticket-flag # notrans
-  name: Ticket Flag
-  name_plural: Ticket Flags
-  sort_mode: Alpha  # notrans
-  masks: 15
-  notes: |
-    Flags that can be set on tickets
-  properties:
-    title: Flag Properties
-    instructions: Properties that can be set on a flag.
-    deletable: false
-    fields:
-      - type: state # notrans
-        name: states # notrans
-        label: Allowed States
-        required: true
-        sort: 1
-        edit_mask: 63
-        configuration:
-            prompt: Allowed Ticket States
-            multiselect: true
-      - type: memo # notrans
-        name: description # notrans
-        label: Description
-        required: false
-        sort: 2
-        edit_mask: 15
-        configuration:
-            rows: 2
-            cols: 40
-            html: false
-            length: 100
-  items: # Note that id doubles as a bit mask for the flag (must be preset)
-    - id: 1 # notrans
-      value: Answered
-      extra: answered
-      status: 3
-      sort: 1
-      configuration:
-        states:
-            open: Open
-        description:|
-            Marked as Answered
-    - id: 2 # notrans
-      value: Onhold
-      extra: onhold
-      status: 3
-      sort: 2
-      configuration:
-        states:
-            open: Open
-        description:|
-            Put on hold
-    - id: 4  # notrans
-      value: Overdue
-      extra: overdue
-      status: 3
-      sort: 3
-      configuration:
-        states:
-            open: Open
-        description:|
-            Marked overdue
diff --git a/include/staff/dynamic-list.inc.php b/include/staff/dynamic-list.inc.php
index e29c1afd29efd7929e6d14da6202a0aaa6c8d758..280d85eccf1a08609215299b7b1abdeb8b87224c 100644
--- a/include/staff/dynamic-list.inc.php
+++ b/include/staff/dynamic-list.inc.php
@@ -17,7 +17,7 @@ if ($list) {
 $info=Format::htmlchars(($errors && $_POST) ? array_merge($info,$_POST) : $info);
 
 ?>
-<form action="?" method="post" id="save">
+<form action="" method="post" id="save">
     <?php csrf_token(); ?>
     <input type="hidden" name="do" value="<?php echo $action; ?>">
     <input type="hidden" name="a" value="<?php echo Format::htmlchars($_REQUEST['a']); ?>">
@@ -150,16 +150,11 @@ $info=Format::htmlchars(($errors && $_POST) ? array_merge($info,$_POST) : $info)
                 <?php } ?>
             </select>
             <?php if ($f->isConfigurable()) { ?>
-                <a class="action-button" style="float:none;overflow:inherit"
-                    href="#ajax.php/form/field-config/<?php
-                        echo $f->get('id'); ?>"
-                    onclick="javascript:
-                        $('#overlay').show();
-                        $('#field-config .body').empty().load($(this).attr('href').substr(1));
-                        $('#field-config').show();
-                        return false;
-                    "><i class="icon-edit"></i> <?php echo __('Config'); ?></a>
-            <?php } ?></td>
+                <a class="action-button field-config"
+                    style="float:none;overflow:inherit"
+                    href="#form/field-config/<?php
+                        echo $f->get('id'); ?>"><i
+                        class="icon-cog"></i> <?php echo __('Config'); ?></a> <?php } ?></td>
             <td>
                 <input type="text" size="20" name="name-<?php echo $id; ?>"
                     value="<?php echo Format::htmlchars($f->get('name'));
@@ -258,15 +253,18 @@ $info=Format::htmlchars(($errors && $_POST) ? array_merge($info,$_POST) : $info)
             <td><input type="text" size="40" name="value-<?php echo $id; ?>"
                 value="<?php echo $i->getValue(); ?>"/>
                 <?php if ($list->hasProperties()) { ?>
-                <a class="action-button" style="float:none;overflow:inherit"
-                    href="#ajax.php/list/<?php
-                        echo $list->getId(); ?>/item/<?php echo $id ?>/properties"
-                    onclick="javascript:
-                        $('#overlay').show();
-                        $('#field-config .body').empty().load($(this).attr('href').substr(1));
-                        $('#field-config').show();
-                        return false;
-                    "><i class="icon-edit"></i> <?php echo __('Properties'); ?></a>
+                   <a class="action-button field-config"
+                       style="float:none;overflow:inherit"
+                       href="#list/<?php
+                        echo $list->getId(); ?>/item/<?php
+                        echo $id ?>/properties"
+                       id="item-<?php echo $id; ?>"
+                    ><?php
+                        echo sprintf('<i class="icon-edit" %s></i> ',
+                                $i->getConfiguration()
+                                ? '': 'style="color:red; font-weight:bold;"');
+                        echo __('Properties');
+                   ?></a>
                 <?php
                 }
 
@@ -335,24 +333,16 @@ $info=Format::htmlchars(($errors && $_POST) ? array_merge($info,$_POST) : $info)
 </p>
 </form>
 
-<div style="display:none;" class="dialog draggable" id="field-config">
-    <div id="popup-loading">
-        <h1><i class="icon-spinner icon-spin icon-large"></i>
-        <?php echo __('Loading ...');?></h1>
-    </div>
-    <div class="body"></div>
-</div>
-
 <script type="text/javascript">
 $(function() {
-    var $this = $('#popup-loading').hide();
-    $(document).ajaxStart( function(event) {
-        console.log(1,event);
-        var $h1 = $this.find('h1');
-        $this.show();
-        $h1.css({'margin-top':$this.height()/3-$h1.height()/3});  // show Loading Div
-    }).ajaxStop ( function(){
-        $this.hide(); // hide loading div
+    $('a.field-config').click( function(e) {
+        e.preventDefault();
+        var $id = $(this).attr('id');
+        var url = 'ajax.php/'+$(this).attr('href').substr(1);
+        $.dialog(url, [200], function (xhr) {
+            $('a#'+$id+' i').removeAttr('style');
+        });
+        return false;
     });
 });
 </script>
diff --git a/include/staff/filter.inc.php b/include/staff/filter.inc.php
index e9a51705a4eb4352b73e0bd92fe91c38781ba527..3bda64e043f56e6106e758ebdcc36988a217e4ce 100644
--- a/include/staff/filter.inc.php
+++ b/include/staff/filter.inc.php
@@ -258,7 +258,7 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
                 <select name="status_id">
                     <option value="">&mdash; <?php echo __('Default'); ?> &mdash;</option>
                     <?php
-                    foreach (TicketStatusList::getAll() as $status) {
+                    foreach (TicketStatusList::getStatuses() as $status) {
                         $name = $status->getName();
                         if (!($isenabled = $status->isEnabled()))
                             $name.=' '.__('(disabled)');
diff --git a/include/staff/footer.inc.php b/include/staff/footer.inc.php
index 68779e589ce2a3c645d71adeb32c24197da7e86f..8a6fc6852f1444cdc4e3a772c032917ab827c2d7 100644
--- a/include/staff/footer.inc.php
+++ b/include/staff/footer.inc.php
@@ -19,9 +19,27 @@ if(is_object($thisstaff) && $thisstaff->isStaff()) { ?>
     <i class="icon-spinner icon-spin icon-3x pull-left icon-light"></i>
     <h1><?php echo __('Loading ...');?></h1>
 </div>
-<div class="dialog" style="display:none;width:650px;" id="popup">
+<div class="dialog draggable" style="display:none;width:650px;" id="popup">
+    <div id="popup-loading">
+        <h1 style="margin-bottom: 20px;"><i class="icon-spinner icon-spin icon-large"></i>
+        <?php echo __('Loading ...');?></h1>
+    </div>
     <div class="body"></div>
 </div>
+<div style="display:none;" class="dialog" id="alert">
+    <h3><i class="icon-warning-sign"></i> <span id="title"></span></h3>
+    <a class="close" href=""><i class="icon-remove-circle"></i></a>
+    <hr/>
+    <div id="body" style="min-height: 20px;"></div>
+    <hr style="margin-top:3em"/>
+    <p class="full-width">
+        <span class="buttons pull-right">
+            <input type="button" value="<?php echo __('OK');?>" class="close">
+        </span>
+     </p>
+    <div class="clear"></div>
+</div>
+
 <script type="text/javascript">
 if ($.support.pjax) {
   $(document).on('click', 'a', function(event) {
diff --git a/include/staff/helptopic.inc.php b/include/staff/helptopic.inc.php
index 0742b32ae7f82b234a66f57e60cb7db50d8031ef..c4190b76c04784287027301aeb6cc094691f17d5 100644
--- a/include/staff/helptopic.inc.php
+++ b/include/staff/helptopic.inc.php
@@ -138,7 +138,7 @@ if ($info['form_id'] == Topic::FORM_USE_PARENT) echo 'selected="selected"';
                 <select name="status_id">
                     <option value="">&mdash; <?php echo __('System Default'); ?> &mdash;</option>
                     <?php
-                    foreach (TicketStatusList::getAll() as $status) {
+                    foreach (TicketStatusList::getStatuses() as $status) {
                         $name = $status->getName();
                         if (!($isenabled = $status->isEnabled()))
                             $name.=' '.__('(disabled)');
diff --git a/include/staff/settings-tickets.inc.php b/include/staff/settings-tickets.inc.php
index e2a46f46717bd6be28553143edd07e987270ef80..b8526ed0c5a03c2cab1b2ed0c82f39af25601c15 100644
--- a/include/staff/settings-tickets.inc.php
+++ b/include/staff/settings-tickets.inc.php
@@ -61,7 +61,8 @@ if(!($maxfileuploads=ini_get('max_file_uploads')))
                 <span>
                 <select name="default_ticket_status_id">
                 <?php
-                foreach (TicketStatusList::getAll(array('open')) as $status) {
+                $criteria = array('states' => array('open'));
+                foreach (TicketStatusList::getStatuses($criteria) as $status) {
                     $name = $status->getName();
                     if (!($isenabled = $status->isEnabled()))
                         $name.=' '.__('(disabled)');
diff --git a/include/staff/templates/dynamic-field-config.tmpl.php b/include/staff/templates/dynamic-field-config.tmpl.php
index 9c1261e9ace8a02000fb188efb51f62900fc951d..fe4116707be969c5c5d4ab2e5ad3510646ce3f91 100644
--- a/include/staff/templates/dynamic-field-config.tmpl.php
+++ b/include/staff/templates/dynamic-field-config.tmpl.php
@@ -1,19 +1,8 @@
     <h3><?php echo __('Field Configuration'); ?> &mdash; <?php echo $field->get('label') ?></h3>
     <a class="close" href=""><i class="icon-remove-circle"></i></a>
     <hr/>
-    <form method="post" action="ajax.php/form/field-config/<?php
-            echo $field->get('id'); ?>" onsubmit="javascript:
-            var form = $(this);
-            $.post(this.action, form.serialize(), function(data, status, xhr) {
-                    if (!data.length) {
-                        form.closest('.dialog').hide();
-                        $('#overlay').hide();
-                    } else {
-                        form.closest('.dialog').empty().append(data);
-                    }
-            });
-            return false;
-            ">
+    <form method="post" action="#form/field-config/<?php
+            echo $field->get('id'); ?>">
         <?php
         echo csrf_token();
         $config = $field->getConfiguration();
diff --git a/include/staff/templates/list-item-properties.tmpl.php b/include/staff/templates/list-item-properties.tmpl.php
index e105bea4fef479b32992bf4fbd28331924191adc..44ce0de055728a163cd805e59ec99a5a8e549fd9 100644
--- a/include/staff/templates/list-item-properties.tmpl.php
+++ b/include/staff/templates/list-item-properties.tmpl.php
@@ -1,19 +1,9 @@
     <h3><?php echo __('Item Properties'); ?> &mdash; <?php echo $item->getValue() ?></h3>
     <a class="close" href=""><i class="icon-remove-circle"></i></a>
     <hr/>
-    <form method="post" action="ajax.php/list/<?php
+    <form method="post" action="#list/<?php
             echo $list->getId(); ?>/item/<?php
-            echo $item->getId(); ?>/properties" onsubmit="javascript:
-            var form = $(this);
-            $.post(this.action, form.serialize(), function(data, status, xhr) {
-                if (!data.length) {
-                    form.closest('.dialog').hide();
-                    $('#overlay').hide();
-                } else {
-                    form.closest('.dialog .body').empty().append(data);
-                }
-            });
-            return false;">
+            echo $item->getId(); ?>/properties">
         <?php
         echo csrf_token();
         $config = $item->getConfiguration();
diff --git a/include/staff/templates/status-options.tmpl.php b/include/staff/templates/status-options.tmpl.php
new file mode 100644
index 0000000000000000000000000000000000000000..5fc39f32ad99c9f6c52b129291545a8f8d7f336d
--- /dev/null
+++ b/include/staff/templates/status-options.tmpl.php
@@ -0,0 +1,61 @@
+<?php
+$actions= array(
+        'close' => array(
+            'action' => __('Close'),
+            'icon'  => 'icon-repeat',
+            'states' => array('closed')
+            ),
+        'resolve' => array(
+            'action' => __('Resolve'),
+            'icon'  => 'icon-ok-circle',
+            'states' => array('resolved')
+            ),
+        'reopen' => array(
+            'action' =>  __('Reopen'),
+            'icon'  => 'icon-undo',
+            'states' => array('open')
+            ),
+        );
+
+foreach($actions as $k => $v) {
+    $criteria = array('states' => $v['states']);
+    if (!($statuses = TicketStatusList::getStatuses($criteria)->all()))
+        continue;
+
+    if ($statuses && count($statuses) > 1) {
+    ?>
+        <span
+            class="action-button"
+            data-dropdown="#action-dropdown-<?php echo $k; ?>">
+            <a id="tickets-<?php echo $k; ?>"
+                class="tickets-action"
+                href="#tickets/status/<?php echo $k; ?>"><i
+                class="<?php echo $v['icon']; ?>"></i> <?php echo $v['action']; ?></a>
+            <i class="icon-caret-down"></i>
+        </span>
+        <div id="action-dropdown-<?php echo $k; ?>"
+            class="action-dropdown anchor-right">
+          <ul>
+            <?php
+            foreach ($statuses as $s) {
+                ?>
+
+             <li>
+                 <a class="no-pjax tickets-action"
+                    href="#tickets/status/<?php echo $k; ?>/<?php
+                    echo $s->getId(); ?>"> <i
+                        class="icon-tag"></i> <?php echo __($s->getName()); ?></a> </li>
+            <?php
+            } ?>
+          </ul>
+        </div>
+    <?php
+    } else {
+    ?>
+        <a id="tickets-<?php echo $k; ?>" class="action-button tickets-action"
+            href="#tickets/status/<?php echo $k; ?>"><i
+            class="<?php echo $v['icon']; ?>"></i> <?php echo $v['action']; ?></a>
+<?php
+    }
+}
+?>
diff --git a/include/staff/templates/ticket-status.tmpl.php b/include/staff/templates/ticket-status.tmpl.php
new file mode 100644
index 0000000000000000000000000000000000000000..8646a05cf439b1f550b6b992fdeef8d72d4df1ee
--- /dev/null
+++ b/include/staff/templates/ticket-status.tmpl.php
@@ -0,0 +1,118 @@
+<?php
+global $cfg;
+
+if (!$info['title'])
+    $info['title'] = 'Change Tickets 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'] ?: ('#tickets/status/'. $state);
+?>
+<div id="ticket-status" style="display:block; margin:5px;">
+    <form method="post" name="status" id="status"
+        action="<?php echo $action; ?>">
+        <table width="100%">
+            <?php
+            if ($info['extra']) {
+                ?>
+            <tbody>
+                <tr><td colspan="2"><strong><?php echo $info['extra'];
+                ?></strong></td> </tr>
+            </tbody>
+            <?php
+            }
+
+            $verb = '';
+            if ($state) {
+                $statuses = TicketStatusList::getStatuses(array('states'=>array($state)))->all();
+                $verb = TicketStateField::getVerb($state);
+            }
+
+            if ($statuses) {
+            ?>
+            <tbody>
+                <tr>
+                    <td colspan=2>
+                        <span>
+                        <?php
+                        if (count($statuses) > 1) { ?>
+                            <strong><?php echo __('Status') ?>:&nbsp;</strong>
+                            <select name="status_id">
+                            <?php
+                            foreach ($statuses as $s) {
+                                echo sprintf('<option value="%d" %s>%s</option>',
+                                        $s->getId(),
+                                        ($info['status_id'] == $s->getId())
+                                         ? 'selected="selected"' : '',
+                                        $s->getName()
+                                        );
+                            }
+                            ?>
+                            </select>
+                            <font class="error">*&nbsp;<?php echo $errors['status_id']; ?></font>
+                        <?php
+                        } elseif ($statuses[0]) {
+                            echo  "<input type='hidden' name='status_id' value={$statuses[0]->getId()} />";
+                        } ?>
+                        </span>
+                    </td>
+                </tr>
+            </tbody>
+            <?php
+            } ?>
+            <tbody>
+                <tr>
+                    <td colspan="2">
+                        <?php
+                        $placeholder = __('Optional reason for status change (internal note)');
+                        ?>
+                        <textarea name="comments" id="comments"
+                            cols="50" rows="3" wrap="soft" style="width:100%"
+                            class="richtext ifhtml no-bar"
+                            placeholder="<?php echo $placeholder; ?>"><?php
+                            echo $info['notes']; ?></textarea>
+                    </td>
+                </tr>
+            </tbody>
+        </table>
+        <hr>
+        <p class="full-width">
+            <span class="buttons" style="float:left">
+                <input type="reset" value="<?php echo __('Reset'); ?>">
+                <input type="button" name="cancel" class="close"
+                value="<?php echo __('Cancel'); ?>">
+            </span>
+            <span class="buttons" style="float:right">
+                <input type="submit" value="<?php
+                echo $verb ?: __('Submit'); ?>">
+            </span>
+         </p>
+    </form>
+</div>
+<div class="clear"></div>
+<script type="text/javascript">
+$(function() {
+    // Copy checked tickets to status form.
+    $('form#tickets input[name="tids[]"]:checkbox:checked')
+    .clone()
+    .prop('type', 'hidden')
+    .removeAttr('class')
+    .appendTo('form#status');
+ });
+</script>
diff --git a/include/staff/ticket-open.inc.php b/include/staff/ticket-open.inc.php
index f9ab0d8f23482bb4ba072ddb60187cd4962844ad..63da455a8bf70b9f52b914e3bda8e9b80517c6e5 100644
--- a/include/staff/ticket-open.inc.php
+++ b/include/staff/ticket-open.inc.php
@@ -330,17 +330,29 @@ if ($_POST)
                 <?php
                 } ?>
 
-            <?php
-            if($thisstaff->canCloseTickets()) { ?>
-                <tr>
-                    <td width="100"><?php echo __('Ticket Status');?>:</td>
-                    <td>
-                        <input type="checkbox" name="ticket_state" value="closed" <?php echo $info['ticket_state']?'checked="checked"':''; ?>>
-                        <b><?php echo __('Close On Response');?></b>&nbsp;<em>(<?php echo __('Only applicable if response is entered');?>)</em>
-                    </td>
-                </tr>
-            <?php
-            } ?>
+            <tr>
+                <td width="100"><?php echo __('Ticket Status');?>:</td>
+                <td>
+                    <select name="statusId">
+                    <?php
+                    $statusId = $info['statusId'] ?: $cfg->getDefaultTicketStatusId();
+                    $states = array('open');
+                    if ($thisstaff->canCloseTickets())
+                        $states = array_merge($states, array('resolved', 'closed'));
+                    foreach (TicketStatusList::getStatuses(
+                                array('states' => $states)) as $s) {
+                        if (!$s->isEnabled()) continue;
+                        $selected = ($statusId == $s->getId());
+                        echo sprintf('<option value="%d" %s>%s</option>',
+                                $s->getId(),
+                                $selected
+                                 ? 'selected="selected"' : '',
+                                __($s->getName()));
+                    }
+                    ?>
+                    </select>
+                </td>
+            </tr>
              <tr>
                 <td width="100"><?php echo __('Signature');?>:</td>
                 <td>
diff --git a/include/staff/ticket-view.inc.php b/include/staff/ticket-view.inc.php
index c1e15703866132c3197a8413cd385fa3585b77d0..2b6bc0477cfb915c4b4426aa399d2174600a0bfb 100644
--- a/include/staff/ticket-view.inc.php
+++ b/include/staff/ticket-view.inc.php
@@ -56,17 +56,20 @@ if($ticket->isOverdue())
             </span>
             <?php
             }
-            if ($thisstaff->canDeleteTickets()) { ?>
-                <a id="ticket-delete" class="action-button confirm-action" href="#delete"><i class="icon-trash"></i> <?php
-                    echo __('Delete'); ?></a>
-            <?php
-            }
             if($thisstaff->canCloseTickets()) {
                 if($ticket->isOpen()) {?>
-                <a id="ticket-close" class="action-button" href="#close"><i class="icon-remove-circle"></i> <?php echo __('Close');?></a>
+                <a class="action-button ticket-action"
+                    href="#tickets/<?php echo $ticket->getId()
+                    ?>/status/close"><i class="icon-repeat"></i> <?php echo __('Close');?></a>
+                <a class="action-button ticket-action"
+                    href="#tickets/<?php echo $ticket->getId()
+                    ?>/status/resolve"><i class="icon-ok-circle"></i> <?php echo __('Resolve');?></a>
                 <?php
                 } else { ?>
-                <a id="ticket-reopen" class="action-button" href="#reopen"><i class="icon-undo"></i> <?php echo __('Reopen');?></a>
+                <a class="action-button ticket-action"
+                    href="#tickets/<?php echo $ticket->getId()
+                    ?>/status/reopen"><i class="icon-undo"></i> <?php echo
+                    __('Reopen');?></a>
                 <?php
                 }
             }
@@ -100,7 +103,15 @@ if($ticket->isOverdue())
                  if($thisstaff->canEditTickets()) { ?>
                     <li><a class="change-user" href="#tickets/<?php
                     echo $ticket->getId(); ?>/change-user"><i class="icon-user"></i> <?php
-                    echo __('Change Ticket Owner'); ?></a></li>
+                    echo __('Change Owner'); ?></a></li>
+                <?php
+                 }
+                 if($thisstaff->canDeleteTickets()) {
+                     ?>
+                    <li><a class="ticket-action" href="#tickets/<?php
+                    echo $ticket->getId(); ?>/status/delete"
+                    data-href="tickets.php"><i class="icon-trash"></i> <?php
+                    echo __('Delete Ticket'); ?></a></li>
                 <?php
                  }
                 if($ticket->isOpen() && ($dept && $dept->isManager($thisstaff))) {
@@ -618,18 +629,21 @@ $tcount+= $ticket->getNumNotes();
                     <select name="reply_status_id">
                     <?php
                     $statusId = $info['reply_status_id'] ?: $ticket->getStatusId();
-                    $states = array('open', 'resolved');
+                    $states = array('open');
                     if ($thisstaff->canCloseTickets())
-                        $states = array_merge($states,
-                                array('closed', 'archived'));
+                        $states = array_merge($states, array('resolved', 'closed'));
 
-                    foreach (TicketStatusList::getAll($states) as $s) {
+                    foreach (TicketStatusList::getStatuses(
+                                array('states' => $states)) as $s) {
                         if (!$s->isEnabled()) continue;
-                        echo sprintf('<option value="%d" %s>%s</option>',
+                        $selected = ($statusId == $s->getId());
+                        echo sprintf('<option value="%d" %s>%s%s</option>',
                                 $s->getId(),
-                                ($statusId == $s->getId())
+                                $selected
                                  ? 'selected="selected"' : '',
-                                $s->getName()
+                                __($s->getName()),
+                                $selected
+                                ? (' ('.__('current').')') : ''
                                 );
                     }
                     ?>
@@ -709,18 +723,18 @@ $tcount+= $ticket->getNumNotes();
                     <select name="note_status_id">
                         <?php
                         $statusId = $info['note_status_id'] ?: $ticket->getStatusId();
-                        $states = array('open', 'resolved');
+                        $states = array('open');
                         if ($thisstaff->canCloseTickets())
-                            $states = array_merge($states,
-                                    array('closed', 'archived'));
-                        foreach (TicketStatusList::getAll($states) as $s) {
+                            $states = array_merge($states, array('resolved', 'closed'));
+                        foreach (TicketStatusList::getStatuses(
+                                    array('states' => $states)) as $s) {
                             if (!$s->isEnabled()) continue;
-                            $selected = $statusId == $s->getID();
+                            $selected = $statusId == $s->getId();
                             echo sprintf('<option value="%d" %s>%s%s</option>',
                                     $s->getId(),
                                     $selected ? 'selected="selected"' : '',
-                                    $s->getName(),
-                                    $selected ? ' '.__('(current)') : ''
+                                    __($s->getName()),
+                                    $selected ? (' ('.__('current').')') : ''
                                     );
                         }
                         ?>
@@ -930,67 +944,6 @@ $tcount+= $ticket->getNumNotes();
     </form>
     <div class="clear"></div>
 </div>
-<div style="display:none;" class="dialog" id="ticket-status">
-    <h3><?php echo sprintf($ticket->isClosed() ? __('Reopen Ticket #%s') : __('Close Ticket #%s'),
-        $ticket->getNumber()); ?></h3>
-    <a class="close" href=""><i class="icon-remove-circle"></i></a>
-    <hr/>
-    <?php
-        echo sprintf(__('Are you sure you want to <b>%s</b> this ticket?'),
-            $ticket->isClosed()?__('REOPEN'):__('CLOSE'));
-        $action = $ticket->isClosed() ? 'reopen': 'close';
-    ?>
-    <br><br>
-    <form action="tickets.php?id=<?php echo $ticket->getId(); ?>" method="post" id="status-form" name="status-form">
-        <?php csrf_token(); ?>
-        <input type="hidden" name="id" value="<?php echo $ticket->getId(); ?>">
-        <input type="hidden" name="a" value="process">
-        <input type="hidden" name="do" value="<?php echo $action; ?>">
-        <fieldset>
-            <div><strong>Ticket Status </strong>
-                <select name="status_id">
-                    <?php
-                    $statusId = $info['status_id'] ?: 0;
-                    if (!$ticket->isOpen())
-                        $states = array('open');
-                    else
-                        $states = array('resolved', 'closed');
-
-                    foreach (TicketStatusList::getAll($states) as $s) {
-                        if (!$s->isEnabled()) continue;
-                        echo sprintf('<option value="%d" %s>%s</option>',
-                                $s->getId(),
-                                ($statusId == $s->getId())
-                                 ? 'selected="selected"' : '',
-                                $s->getName()
-                                );
-                    }
-                    ?>
-                </select>
-                &nbsp;<span class='error'>*&nbsp;<?php echo $errors['status_id']; ?></span>
-            </div>
-        </fieldset>
-        <fieldset>
-            <div style="margin-bottom:0.5em">
-            <em><?php echo __('Reasons for status change (internal note). Optional but highly recommended.');?></em><br>
-            </div>
-            <textarea name="ticket_status_notes" id="ticket_status_notes" cols="50" rows="5" wrap="soft"
-                style="width:100%"
-                class="richtext ifhtml no-bar"><?php echo $info['ticket_status_notes']; ?></textarea>
-        </fieldset>
-        <hr style="margin-top:1em"/>
-        <p class="full-width">
-            <span class="buttons pull-left">
-                <input type="reset" value="<?php echo __('Reset');?>">
-                <input type="button" value="<?php echo __('Cancel');?>" class="close">
-            </span>
-            <span class="buttons pull-right">
-                <input type="submit" value="<?php echo $ticket->isClosed()?__('Reopen'):__('Close'); ?>">
-            </span>
-         </p>
-    </form>
-    <div class="clear"></div>
-</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>
diff --git a/include/staff/tickets.inc.php b/include/staff/tickets.inc.php
index 094d28ded43cf56a50280d5e0413f682f8f9613f..5bd1df943ef8b4c71f5e5e3d7e13f7654ada63f3 100644
--- a/include/staff/tickets.inc.php
+++ b/include/staff/tickets.inc.php
@@ -273,7 +273,7 @@ $query="$qselect $qfrom $qwhere ORDER BY $order_by $order LIMIT ".$pageNav->getS
 $hash = md5($query);
 $_SESSION['search_'.$hash] = $query;
 $res = db_query($query);
-$showing=db_num_rows($res)?$pageNav->showing():"";
+$showing=db_num_rows($res)? ' &mdash; '.$pageNav->showing():"";
 if(!$results_type)
     $results_type = sprintf(__('%s Tickets' /* %s will be a status such as 'open' */),
         mb_convert_case($status, MB_CASE_TITLE));
@@ -327,15 +327,31 @@ if ($results) {
 </div>
 <!-- SEARCH FORM END -->
 <div class="clear"></div>
-<div style="margin-bottom:20px">
-<form action="tickets.php" method="POST" name='tickets'>
+<div style="margin-bottom:20px; padding-top:10px;">
+<div>
+        <div class="pull-left flush-left">
+            <h2><a href="<?php echo Format::htmlchars($_SERVER['REQUEST_URI']); ?>"
+                title="<?php echo __('Refresh'); ?>"><i class="icon-refresh"></i> <?php echo
+                $results_type.$showing; ?></a></h2>
+        </div>
+        <div class="pull-right flush-right">
+            <a id="tickets-delete" class="action-button tickets-action"
+                href="#tickets/status/delete"><i
+            class="icon-trash"></i> <?php echo __('Delete'); ?></a>
+            <?php
+            if ($res && $results && $thisstaff->canManageTickets()) {
+                echo TicketStatus::options();
+            }
+            ?>
+        </div>
+</div>
+<div class="clear" style="margin-bottom:10px;"></div>
+<form action="tickets.php" method="POST" name='tickets' id="tickets">
 <?php csrf_token(); ?>
- <a class="refresh pull-right" href="<?php echo Format::htmlchars($_SERVER['REQUEST_URI']); ?>"><?php echo __('Refresh'); ?></a>
  <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']); ?>" >
  <table class="list" border="0" cellspacing="1" cellpadding="2" width="940">
-    <caption><?php echo $showing; ?>&nbsp;&nbsp;&nbsp;<?php echo $results_type; ?></caption>
     <thead>
         <tr>
             <?php if($thisstaff->canManageTickets()) { ?>
@@ -427,7 +443,8 @@ if ($results) {
                         $sel=true;
                     ?>
                 <td align="center" class="nohover">
-                    <input class="ckb" type="checkbox" name="tids[]" value="<?php echo $row['ticket_id']; ?>" <?php echo $sel?'checked="checked"':''; ?>>
+                    <input class="ckb" type="checkbox" name="tids[]"
+                        value="<?php echo $row['ticket_id']; ?>" <?php echo $sel?'checked="checked"':''; ?>>
                 </td>
                 <?php } ?>
                 <td title="<?php echo $row['email']; ?>" nowrap>
@@ -490,42 +507,6 @@ if ($results) {
         echo '<div>&nbsp;'.__('Page').':'.$pageNav->getPageLinks().'&nbsp;';
         echo '<a class="export-csv no-pjax" href="?a=export&h='
             .$hash.'&status='.$_REQUEST['status'] .'">'.__('Export').'</a>&nbsp;<i class="help-tip icon-question-sign" href="#export"></i></div>';
-    ?>
-        <?php
-         if($thisstaff->canManageTickets()) { ?>
-           <p class="centered" id="actions">
-            <?php
-            $status=$_REQUEST['status']?$_REQUEST['status']:$status;
-            switch (strtolower($status)) {
-                case 'closed': ?>
-                    <input class="button" type="submit" name="reopen" value="<?php echo __('Reopen');?>" >
-                    <?php
-                    break;
-                case 'open':
-                case 'answered':
-                case 'assigned':
-                    ?>
-                    <input class="button" type="submit" name="mark_overdue" value="<?php echo __('Overdue');?>" >
-                    <input class="button" type="submit" name="close" value="<?php echo __('Close');?>">
-                    <?php
-                    break;
-                case 'overdue':
-                    ?>
-                    <input class="button" type="submit" name="close" value="<?php echo __('Close');?>">
-                    <?php
-                    break;
-                default: //search??
-                    ?>
-                    <input class="button" type="submit" name="close" value="<?php echo __('Close');?>" >
-                    <input class="button" type="submit" name="reopen" value="<?php echo __('Reopen');?>">
-            <?php
-            }
-            if($thisstaff->canDeleteTickets()) { ?>
-                <input class="button" type="submit" name="delete" value="<?php echo __('Delete');?>">
-            <?php } ?>
-        </p>
-        <?php
-       }
     } ?>
     </form>
 </div>
@@ -534,20 +515,9 @@ if ($results) {
     <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="close-confirm">
-        <?php echo __('Are you sure want to <b>close</b> selected open tickets?');?>
-    </p>
-    <p class="confirm-action" style="display:none;" id="reopen-confirm">
-        <?php echo __('Are you sure want to <b>reopen</b> selected closed tickets?');?>
-    </p>
     <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>
-    <p class="confirm-action" style="display:none;" id="delete-confirm">
-        <font color="red"><strong><?php echo sprintf(__('Are you sure you want to DELETE %s?'),
-            _N('selected ticket', 'selected tickets', 2));?></strong></font>
-        <br><br><?php echo __('Deleted tickets CANNOT be recovered, including any associated attachments.');?>
-    </p>
     <div><?php echo __('Please confirm to continue.');?></div>
     <hr style="margin-top:1em"/>
     <p class="full-width">
@@ -683,3 +653,22 @@ if ($results) {
         </p>
     </form>
 </div>
+<script type="text/javascript">
+$(function() {
+    $(document).off('.tickets');
+    $(document).on('click.tickets', 'a.tickets-action', function(e) {
+        e.preventDefault();
+        var count = checkbox_checker($('form#tickets'), 1);
+        if (count) {
+            var url = 'ajax.php/'
+            +$(this).attr('href').substr(1)
+            +'?count='+count
+            +'&_uid='+new Date().getTime();
+            $.dialog(url, [201], function (xhr) {
+                window.location.href = window.location.href;
+             });
+        }
+        return false;
+    });
+});
+</script>
diff --git a/include/upgrader/streams/core.sig b/include/upgrader/streams/core.sig
index 4d847feb47a8f3ef48ed15a006600ab660f6190d..30a5aca33dc5932c1e09ede64d418368d54367cb 100644
--- a/include/upgrader/streams/core.sig
+++ b/include/upgrader/streams/core.sig
@@ -1 +1 @@
-b38066879371ed792958b4ba9bc8de4a
+03ff59bf35a58a102e9b32ad33c2839f
diff --git a/include/upgrader/streams/core/8f99b8bf-03ff59bf.cleanup.sql b/include/upgrader/streams/core/8f99b8bf-03ff59bf.cleanup.sql
new file mode 100644
index 0000000000000000000000000000000000000000..bccb432fbf33b17fc27d54fc90490fcfb01e7315
--- /dev/null
+++ b/include/upgrader/streams/core/8f99b8bf-03ff59bf.cleanup.sql
@@ -0,0 +1,7 @@
+DELETE FROM `%TABLE_PREFIX%config`
+    WHERE `namespace`='core' AND `key` = 'random_ticket_ids';
+
+ALTER TABLE `%TABLE_PREFIX%ticket`
+    DROP COLUMN `status`;
+
+OPTIMIZE TABLE `%TABLE_PREFIX%ticket`;
diff --git a/include/upgrader/streams/core/8f99b8bf-b3806687.patch.sql b/include/upgrader/streams/core/8f99b8bf-03ff59bf.patch.sql
similarity index 75%
rename from include/upgrader/streams/core/8f99b8bf-b3806687.patch.sql
rename to include/upgrader/streams/core/8f99b8bf-03ff59bf.patch.sql
index a4cc062be8e8ecd9b19fb3e28ab634b49e03411d..65a50036f8155af515f07dbbe3ba560d96065006 100644
--- a/include/upgrader/streams/core/8f99b8bf-b3806687.patch.sql
+++ b/include/upgrader/streams/core/8f99b8bf-03ff59bf.patch.sql
@@ -1,6 +1,6 @@
 /**
  * @version v1.9.4
- * @signature b38066879371ed792958b4ba9bc8de4a
+ * @signature 03ff59bf35a58a102e9b32ad33c2839f
  * @title Custom Ticket Numbers and Statuses
  *
  * This patch adds support for ticket number sequences to the database
@@ -26,21 +26,18 @@ CREATE TABLE `%TABLE_PREFIX%sequence` (
 ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
 
 SET @random = (SELECT `value` FROM `%TABLE_PREFIX%config`
-    WHERE `namespace` = 'core' AND `key` = 'use_random_ids');
+    WHERE `namespace` = 'core' AND `key` = 'random_ticket_ids');
 
 -- Sequence (id=1) will be loaded in the task file
 INSERT INTO `%TABLE_PREFIX%config` (`namespace`, `key`, `value`)
     VALUES
-    ('core', 'number_format', CASE WHEN @random THEN '######' ELSE '#' END),
-    ('core', 'sequence_id', CASE WHEN @random THEN 0 ELSE 1 END);
-
-DELETE FROM `%TABLE_PREFIX%config`
-WHERE `namespace`='core' AND `key` = 'use_random_ids';
+    ('core', 'number_format', IF(@random, '######', '#')),
+    ('core', 'sequence_id', IF(@random, 0, 1));
 
 ALTER TABLE `%TABLE_PREFIX%help_topic`
-  ADD `flags` int(10) unsigned DEFAULT '0' AFTER `noautoresp`,
-  ADD `sequence_id` int(10) unsigned NOT NULL DEFAULT '0' AFTER `form_id`,
-  ADD `number_format` varchar(32) DEFAULT NULL AFTER `topic`;
+    ADD `flags` int(10) unsigned DEFAULT '0' AFTER `noautoresp`,
+    ADD `sequence_id` int(10) unsigned NOT NULL DEFAULT '0' AFTER `form_id`,
+    ADD `number_format` varchar(32) DEFAULT NULL AFTER `topic`;
 
 ALTER TABLE  `%TABLE_PREFIX%list`
     ADD  `masks` INT UNSIGNED NOT NULL DEFAULT  '0' AFTER  `sort_mode`,
@@ -50,9 +47,9 @@ ALTER TABLE  `%TABLE_PREFIX%list`
 CREATE TABLE IF NOT EXISTS `%TABLE_PREFIX%ticket_status` (
   `id` int(11) NOT NULL AUTO_INCREMENT,
   `name` varchar(60) NOT NULL DEFAULT '',
-  `state` varchar(16) NOT NULL DEFAULT 'open',
+  `state` varchar(16) DEFAULT NULL,
   `mode` int(11) unsigned NOT NULL DEFAULT '0',
-  `flags` int(10) unsigned NOT NULL DEFAULT '0',
+  `flags` int(11) unsigned NOT NULL DEFAULT '0',
   `sort` int(11) unsigned NOT NULL DEFAULT '0',
   `notes` text NOT NULL,
   `created` datetime NOT NULL,
@@ -72,7 +69,13 @@ ALTER TABLE  `%TABLE_PREFIX%ticket`
     ADD  `status_id` INT UNSIGNED NOT NULL DEFAULT  '0' AFTER  `user_email_id`,
     ADD INDEX (`status_id`);
 
+UPDATE `%TABLE_PREFIX%ticket` SET  `status_id` = 3
+    WHERE  `status` = 'closed';
+
+UPDATE `%TABLE_PREFIX%ticket` SET  `status_id` = 1
+        WHERE  `status` = 'open';
+
 -- Finished with patch
 UPDATE `%TABLE_PREFIX%config`
-    SET `value` = 'b38066879371ed792958b4ba9bc8de4a'
+    SET `value` = '03ff59bf35a58a102e9b32ad33c2839f'
     WHERE `key` = 'schema_signature' AND `namespace` = 'core';
diff --git a/include/upgrader/streams/core/8f99b8bf-b3806687.task.php b/include/upgrader/streams/core/8f99b8bf-03ff59bf.task.php
similarity index 92%
rename from include/upgrader/streams/core/8f99b8bf-b3806687.task.php
rename to include/upgrader/streams/core/8f99b8bf-03ff59bf.task.php
index 8c717837e24f0bebaf5ad5796483ba87473d15b7..f7bb36f7db2da78056a307b730543f6cc43ad5b0 100644
--- a/include/upgrader/streams/core/8f99b8bf-b3806687.task.php
+++ b/include/upgrader/streams/core/8f99b8bf-03ff59bf.task.php
@@ -30,6 +30,9 @@ class SequenceLoader extends MigrationTask {
         foreach ($statuses as $s) {
             TicketStatus::__create($s);
         }
+
+        // Initialize MYSQL search backend
+        MysqlSearchBackend::__init();
     }
 }
 
diff --git a/main.inc.php b/main.inc.php
index 4f4b01bfbe404fad9b44767eaa4b49acb9755360..026e440ca84cc904d8a5ebcf8e6b5101cd81d41e 100644
--- a/main.inc.php
+++ b/main.inc.php
@@ -43,4 +43,12 @@ $_POST=Format::strip_slashes($_POST);
 $_GET=Format::strip_slashes($_GET);
 $_REQUEST=Format::strip_slashes($_REQUEST);
 }
+
+// extract system messages
+$errors = array();
+$msg=$warn=$sysnotice='';
+if ($_SESSION['::sysmsgs']) {
+    extract($_SESSION['::sysmsgs']);
+    unset($_SESSION['::sysmsgs']);
+}
 ?>
diff --git a/scp/ajax.php b/scp/ajax.php
index 664e12566b00bbcb03ab2e33511b7396b18caa55..cdeec76d9bb1f7ef5b5e3ae3b86fb009b47e4fe9 100644
--- a/scp/ajax.php
+++ b/scp/ajax.php
@@ -135,11 +135,15 @@ $dispatcher = patterns('',
         url_get('^(?P<tid>\d+)/add-collaborator/(?P<uid>\d+)$', 'addCollaborator'),
         url_get('^(?P<tid>\d+)/add-collaborator/auth:(?P<bk>\w+):(?P<id>.+)$', 'addRemoteCollaborator'),
         url('^(?P<tid>\d+)/add-collaborator$', 'addCollaborator'),
-        url_get('^lookup', 'lookup'),
-        url_get('^search', 'search'),
         url_get('^(?P<tid>\d+)/forms/manage$', 'manageForms'),
         url_post('^(?P<tid>\d+)/forms/manage$', 'updateForms'),
-        url_get('^(?P<tid>\d+)/canned-resp/(?P<cid>\w+).(?P<format>json|txt)', 'cannedResponse')
+        url_get('^(?P<tid>\d+)/canned-resp/(?P<cid>\w+).(?P<format>json|txt)', 'cannedResponse'),
+        url_get('^(?P<tid>\d+)/status/(?P<status>\w+)$', 'changeTicketStatus'),
+        url_post('^(?P<tid>\d+)/status/(?P<status>\w+)$', 'setTicketStatus'),
+        url_get('^status/(?P<status>\w+)(?:/(?P<sid>\d+))?$', 'changeTicketsStatus'),
+        url_post('^status/(?P<state>\w+)$', 'setTicketsStatus'),
+        url_get('^lookup', 'lookup'),
+        url_get('^search', 'search')
     )),
     url('^/collaborators/', patterns('ajax.tickets.php:TicketsAjaxAPI',
         url_get('^(?P<cid>\d+)/view$', 'viewCollaborator'),
diff --git a/scp/js/scp.js b/scp/js/scp.js
index eb71febb5e78f2b9613629264d115266b39b7b70..953712f43b368377454b5ff028b553bb40330c96 100644
--- a/scp/js/scp.js
+++ b/scp/js/scp.js
@@ -16,18 +16,22 @@ function checkbox_checker(formObj, min, max) {
         msg=__("You're limited to only {0} selections.\n") .replace('{0}', max);
         msg=msg + __("You have made {0} selections.\n").replace('{0}', checked);
         msg=msg + __("Please remove {0} selection(s).").replace('{0}', checked-max);
-        alert(msg)
+        $.sysAlert(__('Alert'), msg);
+
         return (false);
     }
 
     if (checked< min ){
-        alert(__("Please make at least {0} selections. {1} checked so far.")
-            .replace('{0}', min)
-            .replace('{1}', checked));
+        $.sysAlert( __('Alert'),
+                __("Please make at least {0} selections. {1} checked so far.")
+                .replace('{0}', min)
+                .replace('{1}', checked)
+                );
+
         return (false);
     }
 
-    return (true);
+    return checked;
 }
 
 
@@ -526,12 +530,20 @@ $.dialog = function (url, codes, cb, options) {
     if (codes && !$.isArray(codes))
         codes = [codes];
 
-    $('.dialog#popup .body').load(url, function () {
+    var $popup = $('.dialog#popup');
+
+    $('#overlay').show();
+    $('div.body', $popup).empty().hide();
+    $('div#popup-loading', $popup).show();
+    $popup.show();
+    $('div.body', $popup).load(url, function () {
+        $('div#popup-loading', $popup).hide();
         $('#overlay').show();
-        $('.dialog#popup').show({
+        $('div.body', $popup).show({
             duration: 0,
             complete: function() { if (options.onshow) options.onshow(); }
         });
+        $popup.show();
         $(document).off('.dialog');
         $(document).on('submit.dialog', '.dialog#popup form', function(e) {
             e.preventDefault();
@@ -563,6 +575,18 @@ $.dialog = function (url, codes, cb, options) {
     if (options.onload) { options.onload(); }
  };
 
+$.sysAlert = function (title, msg, cb) {
+    var $dialog =  $('.dialog#alert');
+    if ($dialog.length) {
+        $('#overlay').show();
+        $('#title', $dialog).html(title);
+        $('#body', $dialog).html(msg);
+        $dialog.show();
+    } else {
+        alert(msg);
+    }
+};
+
 $.userLookup = function (url, cb) {
     $.dialog(url, 201, function (xhr) {
         var user = $.parseJSON(xhr.responseText);
diff --git a/scp/js/ticket.js b/scp/js/ticket.js
index ba70ec2df65f1a77b6f76cbf65163383c9979e19..ca5f3957e25da6f1218aa102e27725f6c51b17e3 100644
--- a/scp/js/ticket.js
+++ b/scp/js/ticket.js
@@ -381,7 +381,7 @@ var ticket_onload = function($) {
     autoLock.Init();
 
     /*** Ticket Actions **/
-    //print options
+    //print options TODO: move to backend
     $('a#ticket-print').click(function(e) {
         e.preventDefault();
         $('#overlay').show();
@@ -389,11 +389,18 @@ var ticket_onload = function($) {
         return false;
     });
 
-    //ticket status (close & reopen) xxx: move to backend ticket-action
-    $('a#ticket-close, a#ticket-reopen').click(function(e) {
+
+    $(document).off('.ticket-action');
+    $(document).on('click.ticket-action', 'a.ticket-action', function(e) {
         e.preventDefault();
-        $('#overlay').show();
-        $('.dialog#ticket-status').show();
+        var url = 'ajax.php/'
+        +$(this).attr('href').substr(1)
+        +'?_uid='+new Date().getTime();
+        var $redirect = $(this).data('href');
+        $.dialog(url, [201], function (xhr) {
+            window.location.href = $redirect ? $redirect : window.location.href;
+        });
+
         return false;
     });
 
diff --git a/scp/lists.php b/scp/lists.php
index 02b41d117988466a5a58f92ff31be4d409541e08..b3c424de7e15af1ecd0bbfe7428b57c013776330 100644
--- a/scp/lists.php
+++ b/scp/lists.php
@@ -95,9 +95,11 @@ if($_POST) {
                 if ($errors)
                      $errors['err'] = $errors['err'] ?: sprintf(__('Unable to update %s. Correct error(s) below and try again!'),
                         __('custom list items'));
-                else
+                else {
+                    $list->_items = null;
                     $msg = sprintf(__('Successfully updated %s'),
                         __('this custom list'));
+                }
 
             } elseif ($errors)
                 $errors['err'] = $errors['err'] ?: sprintf(__('Unable to update %s. Correct error(s) below and try again!'),
diff --git a/scp/staff.inc.php b/scp/staff.inc.php
index 9330717e3dc86bc495d8b22e0b0c2066001247a9..d4399c74b8fa94f9e049c84bf4be855c1d44df66 100644
--- a/scp/staff.inc.php
+++ b/scp/staff.inc.php
@@ -110,9 +110,6 @@ $_SESSION['TZ_DST']=$thisstaff->observeDaylight();
 
 define('PAGE_LIMIT', $thisstaff->getPageLimit()?$thisstaff->getPageLimit():DEFAULT_PAGE_LIMIT);
 
-//Clear some vars. we use in all pages.
-$errors=array();
-$msg=$warn=$sysnotice='';
 $tabs=array();
 $submenu=array();
 $exempt = in_array(basename($_SERVER['SCRIPT_NAME']), array('logout.php', 'ajax.php', 'logs.php', 'upgrade.php'));
diff --git a/scp/tickets.php b/scp/tickets.php
index f3502c96a2666cfcbdd8868d0a276ad03055c4cd..17235eb62f1368744bc5a5164bb42cde496e600f 100644
--- a/scp/tickets.php
+++ b/scp/tickets.php
@@ -201,52 +201,6 @@ if($_POST && !$errors):
             break;
         case 'process':
             switch(strtolower($_POST['do'])):
-                case 'close':
-                    if(!$thisstaff->canCloseTickets()) {
-                        $errors['err'] = __('Permission Denied. You are not allowed to close tickets.');
-                    } elseif($ticket->isClosed()) {
-                        $errors['err'] = __('Ticket is already closed!');
-                    } elseif (!($status=TicketStatus::lookup($_POST['status_id']))) {
-                        $errors['err'] = __('Unknown status');
-                    } elseif (!in_array($status->getState(), array('resolved', 'closed'))) {
-                        $errors['err'] = __('Invalid status');
-                    } elseif ($ticket->setStatus($status)) {
-                        $msg=sprintf(__('Ticket #%s status set to CLOSED'),$ticket->getNumber());
-                        if($_POST['ticket_status_notes']) {
-                            $ticket->logNote(
-                                    __('Ticket Closed'),
-                                    $_POST['ticket_status_notes'],
-                                    $thisstaff);
-                        }
-                        //Going back to main listing.
-                        TicketLock::removeStaffLocks($thisstaff->getId(), $ticket->getId());
-                        $page=$ticket=null;
-                    } else {
-                        $errors['err']=__('Problems closing the ticket. Try again');
-                    }
-                    break;
-                case 'reopen':
-                    //if staff can close or create tickets ...then assume they can reopen.
-                    if (!$thisstaff->canCloseTickets() && !$thisstaff->canCreateTickets()) {
-                        $errors['err']=__('Permission Denied. You are not allowed to reopen tickets.');
-                    } elseif ($ticket->isOpen()) {
-                        $errors['err'] = __('Ticket is already open!');
-                    } elseif (!($status=TicketStatus::lookup($_POST['status_id']))) {
-                        $errors['err'] = __('Unknown status');
-                    } elseif (strcasecmp($status->getState(), 'open')) {
-                        $errors['err'] = __('Invalid status');
-                    } elseif ($ticket->setStatus($status)) {
-                        $msg=__('Ticket Reopened');
-                        if($_POST['ticket_status_notes']) {
-                            $ticket->logNote(
-                                    __('Ticket Reopened'),
-                                    $_POST['ticket_status_notes'],
-                                    $thisstaff);
-                        }
-                    } else {
-                        $errors['err']=__('Problems reopening the ticket. Try again');
-                    }
-                    break;
                 case 'release':
                     if(!$ticket->isAssigned() || !($assigned=$ticket->getAssigned())) {
                         $errors['err'] = __('Ticket is not assigned!');
@@ -336,21 +290,6 @@ if($_POST && !$errors):
                         $errors['err'] = 'Unable to change tiket ownership. Try again';
                     }
                     break;
-                case 'delete': // Dude what are you trying to hide? bad customer support??
-                    if(!$thisstaff->canDeleteTickets()) {
-                        $errors['err']=__('Permission Denied. You are not allowed to DELETE tickets!!');
-                    } elseif($ticket->delete()) {
-                        $msg=sprintf(__('Ticket #%s deleted successfully'),$ticket->getNumber());
-                        //Log a debug note
-                        $ost->logDebug(sprintf(__('Ticket #%s deleted'),$ticket->getNumber()),
-                                sprintf(__('Ticket #%1$s deleted by %2$s'),
-                                    $ticket->getNumber(), $thisstaff->getName())
-                                );
-                        $ticket=null; //clear the object.
-                    } else {
-                        $errors['err']=__('Problems deleting the ticket. Try again');
-                    }
-                    break;
                 default:
                     $errors['err']=__('You must select action to perform');
             endswitch;
@@ -363,90 +302,6 @@ if($_POST && !$errors):
     }elseif($_POST['a']) {
 
         switch($_POST['a']) {
-            case 'mass_process':
-                if(!$thisstaff->canManageTickets())
-                    $errors['err']=__('You do not have permission to mass manage tickets. Contact admin for such access');
-                elseif(!$_POST['tids'] || !is_array($_POST['tids']))
-                    $errors['err']=sprintf(__('You must select at least %s.'),
-                        __('one ticket'));
-                else {
-                    $count=count($_POST['tids']);
-                    $i = 0;
-                    switch(strtolower($_POST['do'])) {
-                        case 'reopen':
-                            if($thisstaff->canCloseTickets() || $thisstaff->canCreateTickets()) {
-                                $note=sprintf(__('Ticket reopened by %s'),$thisstaff->getName());
-                                foreach($_POST['tids'] as $k=>$v) {
-                                    if(($t=Ticket::lookup($v)) && $t->isClosed() && @$t->reopen()) {
-                                        $i++;
-                                        $t->logNote(__('Ticket Reopened'), $note, $thisstaff);
-                                    }
-                                }
-
-                                if($i==$count)
-                                    $msg = sprintf(__('Successfully reopened %s'),
-                                        _N('selected ticket', 'selected tickets', $count));
-                                elseif($i)
-                                    $warn = sprintf(__('%1$d of %2$d %3$s reopened'),$i, $count,
-                                        _N('selected ticket', 'selected tickets', $count));
-                                else
-                                    $errors['err'] = sprintf(__('Unable to reopen %s'),
-                                        _N('selected ticket', 'selected tickets', $count));
-                            } else {
-                                $errors['err'] = __('You do not have permission to reopen tickets');
-                            }
-                            break;
-                        case 'close':
-                            $errors['err'] = 'Support coming soon!';
-                            break;
-                        case 'mark_overdue':
-                            $note=sprintf(__('Ticket flagged as overdue by %s'),$thisstaff->getName());
-                            foreach($_POST['tids'] as $k=>$v) {
-                                if(($t=Ticket::lookup($v)) && !$t->isOverdue() && $t->markOverdue()) {
-                                    $i++;
-                                    $t->logNote(__('Ticket Marked Overdue'), $note, $thisstaff);
-                                }
-                            }
-
-                            if($i==$count)
-                                $msg = sprintf(__('Selected tickets (%d) marked overdue'), $i);
-                            elseif($i)
-                                $warn = sprintf(__('%1$d of %2$d selected tickets marked overdue'), $i, $count);
-                            else
-                                $errors['err'] = __('Unable to flag selected tickets as overdue');
-                            break;
-                        case 'delete':
-                            if($thisstaff->canDeleteTickets()) {
-                                foreach($_POST['tids'] as $k=>$v) {
-                                    if(($t=Ticket::lookup($v)) && @$t->delete()) $i++;
-                                }
-
-                                //Log a warning
-                                if($i) {
-                                    $log = sprintf(_S('%1$s (%2$s) just deleted %3$d ticket(s)'),
-                                            $thisstaff->getName(), $thisstaff->getUserName(), $i);
-                                    $ost->logWarning(_S('Tickets deleted'), $log, false);
-
-                                }
-
-                                if($i==$count)
-                                    $msg = sprintf(__('Successfully deleted %s'),
-                                        _N('selected ticket', 'selected tickets', $count));
-                                elseif($i)
-                                    $warn = sprintf(__('%1$d of %2$d %3$s deleted'),$i, $count,
-                                        _N('selected ticket', 'selected tickets', $count));
-                                else
-                                    $errors['err'] = sprintf(__('Unable to delete %s'),
-                                        _N('selected ticket', 'selected tickets', $count));
-                            } else {
-                                $errors['err'] = __('You do not have permission to delete tickets');
-                            }
-                            break;
-                        default:
-                            $errors['err']=__('Unknown action - get technical help.');
-                    }
-                }
-                break;
             case 'open':
                 $ticket=null;
                 if(!$thisstaff || !$thisstaff->canCreateTickets()) {
@@ -489,7 +344,8 @@ if($cfg->showAnsweredTickets()) {
                         (!$_REQUEST['status'] || $_REQUEST['status']=='open'));
 } else {
 
-    if($stats) {
+    if ($stats) {
+
         $nav->addSubMenu(array('desc'=>$open_name.' ('.number_format($stats['open']).')',
                                'title'=>__('Open Tickets'),
                                'href'=>'tickets.php',
@@ -534,13 +390,14 @@ if($thisstaff->showAssignedOnly() && $stats['closed']) {
                         ($_REQUEST['status']=='closed'));
 } else {
 
-    $nav->addSubMenu(array('desc'=>sprintf(__('Resolved (%s)'), number_format($stats['resolved'])),
-                           'title'=>__('Resolved Tickets'),
-                           'href'=>'tickets.php?status=resolved',
-                           'iconclass'=>'closedTickets'),
-                        ($_REQUEST['status']=='resolved'));
+    if ($stats['resolved'])
+        $nav->addSubMenu(array('desc' => __('Resolved').' ('.number_format($stats['resolved']).')',
+                               'title'=>__('Resolved Tickets'),
+                               'href'=>'tickets.php?status=resolved',
+                               'iconclass'=>'closedTickets'),
+                            ($_REQUEST['status']=='resolved'));
 
-    $nav->addSubMenu(array('desc'=>sprintf(__('Closed Tickets (%s)'), number_format($stats['closed'])),
+    $nav->addSubMenu(array('desc' => __('Closed').' ('.number_format($stats['closed']).')',
                            'title'=>__('Closed Tickets'),
                            'href'=>'tickets.php?status=closed',
                            'iconclass'=>'closedTickets'),
diff --git a/setup/inc/class.installer.php b/setup/inc/class.installer.php
index 1e3ae346e35784331d16d4ebc3d2c895c3746f58..0c7ea1f5b2a7029062e40969e3312cccd4a8be08 100644
--- a/setup/inc/class.installer.php
+++ b/setup/inc/class.installer.php
@@ -115,8 +115,8 @@ class Installer extends SetupWizard {
 
         /*************** We're ready to install ************************/
         define('ADMIN_EMAIL',$vars['admin_email']); //Needed to report SQL errors during install.
-        define('PREFIX',$vars['prefix']); //Table prefix
-        Bootstrap::defineTables(PREFIX);
+        define('TABLE_PREFIX',$vars['prefix']); //Table prefix
+        Bootstrap::defineTables(TABLE_PREFIX);
         Bootstrap::loadCode();
 
         $debug = true; // Change it to false to squelch SQL errors.
@@ -157,23 +157,25 @@ class Installer extends SetupWizard {
             $i18n = new Internationalization($vars['lang_id']);
             $i18n->loadDefaultData();
 
-            $sql='SELECT `id` FROM '.PREFIX.'sla ORDER BY `id` LIMIT 1';
+            Signal::send('system.install', $this);
+
+            $sql='SELECT `id` FROM '.TABLE_PREFIX.'sla ORDER BY `id` LIMIT 1';
             $sla_id_1 = db_result(db_query($sql, false));
 
-            $sql='SELECT `dept_id` FROM '.PREFIX.'department ORDER BY `dept_id` LIMIT 1';
+            $sql='SELECT `dept_id` FROM '.TABLE_PREFIX.'department ORDER BY `dept_id` LIMIT 1';
             $dept_id_1 = db_result(db_query($sql, false));
 
-            $sql='SELECT `tpl_id` FROM '.PREFIX.'email_template_group ORDER BY `tpl_id` LIMIT 1';
+            $sql='SELECT `tpl_id` FROM '.TABLE_PREFIX.'email_template_group ORDER BY `tpl_id` LIMIT 1';
             $template_id_1 = db_result(db_query($sql, false));
 
-            $sql='SELECT `group_id` FROM '.PREFIX.'groups ORDER BY `group_id` LIMIT 1';
+            $sql='SELECT `group_id` FROM '.TABLE_PREFIX.'groups ORDER BY `group_id` LIMIT 1';
             $group_id_1 = db_result(db_query($sql, false));
 
-            $sql='SELECT `value` FROM '.PREFIX.'config WHERE namespace=\'core\' and `key`=\'default_timezone_id\' LIMIT 1';
+            $sql='SELECT `value` FROM '.TABLE_PREFIX.'config WHERE namespace=\'core\' and `key`=\'default_timezone_id\' LIMIT 1';
             $default_timezone = db_result(db_query($sql, false));
 
             //Create admin user.
-            $sql='INSERT INTO '.PREFIX.'staff SET created=NOW() '
+            $sql='INSERT INTO '.TABLE_PREFIX.'staff SET created=NOW() '
                 .", isactive=1, isadmin=1, group_id='$group_id_1', dept_id='$dept_id_1'"
                 .", timezone_id='$default_timezone', max_page_size=25"
                 .', email='.db_input($vars['admin_email'])
@@ -189,14 +191,14 @@ class Installer extends SetupWizard {
             //Create default emails!
             $email = $vars['email'];
             list(,$domain)=explode('@',$vars['email']);
-            $sql='INSERT INTO '.PREFIX.'email (`name`,`email`,`created`,`updated`) VALUES '
+            $sql='INSERT INTO '.TABLE_PREFIX.'email (`name`,`email`,`created`,`updated`) VALUES '
                     ." ('Support','$email',NOW(),NOW())"
                     .",('osTicket Alerts','alerts@$domain',NOW(),NOW())"
                     .",('','noreply@$domain',NOW(),NOW())";
             $support_email_id = db_query($sql, false) ? db_insert_id() : 0;
 
 
-            $sql='SELECT `email_id` FROM '.PREFIX."email WHERE `email`='alerts@$domain' LIMIT 1";
+            $sql='SELECT `email_id` FROM '.TABLE_PREFIX."email WHERE `email`='alerts@$domain' LIMIT 1";
             $alert_email_id = db_result(db_query($sql, false));
 
             //Create config settings---default settings!
@@ -248,7 +250,7 @@ class Installer extends SetupWizard {
 
         /************* Make the system happy ***********************/
 
-        $sql='UPDATE '.PREFIX."email SET dept_id=$dept_id_1";
+        $sql='UPDATE '.TABLE_PREFIX."email SET dept_id=$dept_id_1";
         db_query($sql, false);
 
         global $cfg;
@@ -271,7 +273,7 @@ class Installer extends SetupWizard {
 
         //Log a message.
         $msg=__("Congratulations osTicket basic installation completed!\n\nThank you for choosing osTicket!");
-        $sql='INSERT INTO '.PREFIX.'syslog SET created=NOW(), updated=NOW(), log_type="Debug" '
+        $sql='INSERT INTO '.TABLE_PREFIX.'syslog SET created=NOW(), updated=NOW(), log_type="Debug" '
             .', title="osTicket installed!"'
             .', log='.db_input($msg)
             .', ip_address='.db_input($_SERVER['REMOTE_ADDR']);
diff --git a/setup/inc/streams/core/install-mysql.sql b/setup/inc/streams/core/install-mysql.sql
index ac5c3783f3ecd1fcb0b646f2e1e8a7d9a8c17056..4879e53ce514f2aa1f4ba813b1fd967b3ae3f2cb 100644
--- a/setup/inc/streams/core/install-mysql.sql
+++ b/setup/inc/streams/core/install-mysql.sql
@@ -599,7 +599,6 @@ CREATE TABLE `%TABLE_PREFIX%ticket` (
   `email_id` int(11) unsigned NOT NULL default '0',
   `flags` int(10) unsigned NOT NULL default '0',
   `ip_address` varchar(64) NOT NULL default '',
-  `status` enum('open','closed') NOT NULL default 'open',
   `source` enum('Web','Email','Phone','API','Other') NOT NULL default 'Other',
   `isoverdue` tinyint(1) unsigned NOT NULL default '0',
   `isanswered` tinyint(1) unsigned NOT NULL default '0',
@@ -614,7 +613,6 @@ CREATE TABLE `%TABLE_PREFIX%ticket` (
   KEY `user_id` (`user_id`),
   KEY `dept_id` (`dept_id`),
   KEY `staff_id` (`staff_id`),
-  KEY `status` (`status`),
   KEY `team_id` (`team_id`),
   KEY `status_id` (`status_id`),
   KEY `created` (`created`),
@@ -679,9 +677,9 @@ DROP TABLE IF EXISTS `%TABLE_PREFIX%ticket_status`;
 CREATE TABLE IF NOT EXISTS `%TABLE_PREFIX%ticket_status` (
   `id` int(11) NOT NULL AUTO_INCREMENT,
   `name` varchar(60) NOT NULL DEFAULT '',
-  `state` varchar(16) NOT NULL DEFAULT 'open',
+  `state` varchar(16) DEFAULT NULL,
   `mode` int(11) unsigned NOT NULL DEFAULT '0',
-  `flags` int(10) unsigned NOT NULL DEFAULT '0',
+  `flags` int(11) unsigned NOT NULL DEFAULT '0',
   `sort` int(11) unsigned NOT NULL DEFAULT '0',
   `notes` text NOT NULL,
   `created` datetime NOT NULL,