diff --git a/include/class.orm.php b/include/class.orm.php
index 43df8e29f3ad0acf11fc684c89245f5fd6223d7f..6876be7370dc6a583104663e2bf94cc9157fd6ce 100644
--- a/include/class.orm.php
+++ b/include/class.orm.php
@@ -43,9 +43,16 @@ class ModelMeta implements ArrayAccess {
 
     function __construct($model) {
         $this->model = $model;
-        $meta = $model::$meta + self::$base;
+        $parent = get_parent_class($model);
 
-        // TODO: Merge ModelMeta from parent model (if inherited)
+        // Merge ModelMeta from parent model (if inherited)
+        if (is_subclass_of($parent, 'VerySimpleModel')) {
+            $parent::_inspect();
+            $meta = $parent::$meta->extend($model::$meta);
+        }
+        else {
+            $meta = $model::$meta + self::$base;
+        }
 
         if (!$meta['table'])
             throw new OrmConfigurationException(
@@ -67,11 +74,19 @@ class ModelMeta implements ArrayAccess {
             $meta['joins'] = array();
         foreach ($meta['joins'] as $field => &$j) {
             $this->processJoin($j);
+            if ($j['local'])
+                $meta['foreign_keys'][$j['local']] = $field;
         }
         unset($j);
         $this->base = $meta;
     }
 
+    function extend($meta) {
+        if ($meta instanceof self)
+            $meta = $meta->base;
+        return $meta + $this->base + self::$base;
+    }
+
     function processJoin(&$j) {
         $constraint = array();
         if (isset($j['reverse'])) {
@@ -266,11 +281,15 @@ class VerySimpleModel {
             || isset(static::$meta['joins'][$field]);
     }
     function __unset($field) {
-        unset($this->ht[$field]);
+        if ($this->__isset($field))
+            unset($this->ht[$field]);
+        else
+            unset($this->{$field});
     }
 
     function set($field, $value) {
         // Update of foreign-key by assignment to model instance
+        $related = false;
         if (isset(static::$meta['joins'][$field])) {
             // XXX: This is likely not necessary
             if (!isset(static::$meta['joins'][$field]['fkey']))
@@ -282,9 +301,9 @@ class VerySimpleModel {
                 return;
             }
             if ($value === null) {
+                $this->ht[$field] = $value;
                 if (in_array($j['local'], static::$meta['pk'])) {
                     // Reverse relationship — don't null out local PK
-                    $this->ht[$field] = $value;
                     return;
                 }
                 // Pass. Set local field to NULL in logic below
@@ -307,12 +326,23 @@ class VerySimpleModel {
             // Capture the foreign key id value
             $field = $j['local'];
         }
+        // elseif $field is in a relationship, adjust the relationship
+        elseif (isset(static::$meta['foreign_keys'][$field])) {
+            // meta->foreign_keys->{$field} points to the property of the
+            // foreign object. For instance 'object_id' points to 'object'
+            $related = static::$meta['foreign_keys'][$field];
+        }
         $old = isset($this->ht[$field]) ? $this->ht[$field] : null;
         if ($old != $value) {
             // isset should not be used here, because `null` should not be
             // replaced in the dirty array
             if (!array_key_exists($field, $this->dirty))
                 $this->dirty[$field] = $old;
+            if ($related)
+                // $related points to a foreign object propery. If setting a
+                // new object_id value, the relationship to object should be
+                // cleared and rebuilt
+                unset($this->ht[$related]);
         }
         $this->ht[$field] = $value;
     }
@@ -376,6 +406,9 @@ class VerySimpleModel {
      * no such instance exists.
      */
     static function lookup($criteria) {
+        // Autoinsepct model
+        static::_inspect();
+
         // Model::lookup(1), where >1< is the pk value
         if (!is_array($criteria)) {
             $criteria = array();
diff --git a/include/class.staff.php b/include/class.staff.php
index 457ab1787462e340b4a9610efb8ff72397732d5e..d23a3704a07a9349e29e8f836f954ec56dce2b4b 100644
--- a/include/class.staff.php
+++ b/include/class.staff.php
@@ -40,8 +40,6 @@ implements AuthenticatedUser, EmailContact, TemplateVariable {
                 'constraint' => array('group_id' => 'Group.id'),
             ),
             'teams' => array(
-                'null' => true,
-                'list' => true,
                 'reverse' => 'TeamMember.staff',
             ),
         ),
diff --git a/include/class.task.php b/include/class.task.php
index 36c858e6bcba083bfd148989a040f9910e3341ff..6365d6c57a0cb450ccc668f875445164991ba89b 100644
--- a/include/class.task.php
+++ b/include/class.task.php
@@ -49,6 +49,13 @@ class TaskModel extends VerySimpleModel {
                 'constraint' => array('id' => 'TaskCData.task_id'),
                 'list' => false,
             ),
+            'ticket' => array(
+                'constraint' => array(
+                    'object_type' => "'T'",
+                    'object_id' => 'Ticket.ticket_id',
+                ),
+                'null' => true,
+            ),
         ),
     );
 
diff --git a/include/class.ticket.php b/include/class.ticket.php
index cc9cbd703ab439d8224634e28ca8a80f7b724bce..57bfe72fe76c3cca15e7e7813929c390d4b724d5 100644
--- a/include/class.ticket.php
+++ b/include/class.ticket.php
@@ -53,14 +53,20 @@ class TicketModel extends VerySimpleModel {
             'dept' => array(
                 'constraint' => array('dept_id' => 'Dept.id'),
             ),
+            'events' => array(
+                'reverse' => 'TicketEvent.ticket',
+            ),
             'sla' => array(
-                'constraint' => array('sla_id' => 'SlaModel.id'),
+                'constraint' => array('sla_id' => 'Sla.id'),
                 'null' => true,
             ),
             'staff' => array(
                 'constraint' => array('staff_id' => 'Staff.staff_id'),
                 'null' => true,
             ),
+            'tasks' => array(
+                'reverse' => 'Task.ticket',
+            ),
             'team' => array(
                 'constraint' => array('team_id' => 'Team.team_id'),
                 'null' => true,
@@ -107,7 +113,6 @@ class TicketModel extends VerySimpleModel {
                 'desc'  =>
                 /* @trans */ 'Ability to assign tickets to agents or teams'),
             self::PERM_TRANSFER => array(
-
                 'title' =>
                 /* @trans */ 'Transfer',
                 'desc'  =>
@@ -210,88 +215,30 @@ class TicketCData extends VerySimpleModel {
 }
 TicketCData::$meta['table'] = TABLE_PREFIX . 'ticket__cdata';
 
+class Ticket extends TicketModel
+implements RestrictedAccess, Threadable {
 
-class Ticket
-implements RestrictedAccess, Threadable, TemplateVariable {
-
-    var $id;
-    var $number;
-
-    var $ht;
+    static $meta = array(
+        'select_related' => array('topic', 'staff', 'user', 'team', 'dept', 'sla', 'thread'),
+    );
 
     var $lastMsgId;
 
-    var $status;
-    var $dept;  //Dept obj
-    var $sla;   // SLA obj
-    var $staff; //Staff obj
-    var $client; //Client Obj
-    var $team;  //Team obj
-    var $topic; //Topic obj
-    var $tlock; //TicketLock obj
-
-    var $thread; //Thread obj.
-
-    function Ticket($id) {
-        $this->id = 0;
-        $this->load($id);
-    }
-
-    function load($id=0) {
-
-        if (!$id && !($id=$this->getId()))
-            return false;
-
-        $sql='SELECT  ticket.*, thread.id as thread_id, ticket.lock_id, dept.name as dept_name '
-            .' ,count(distinct attach.id) as attachments'
-            .' ,count(distinct task.id) as tasks'
-            .' FROM '.TICKET_TABLE.' ticket '
-            .' LEFT JOIN '.DEPT_TABLE.' dept ON (ticket.dept_id=dept.id) '
-            .' LEFT JOIN '.SLA_TABLE.' sla ON (ticket.sla_id=sla.id AND sla.flags & 1 = 1) '
-            .' LEFT JOIN '.LOCK_TABLE.' tlock
-                ON ( ticket.lock_id=tlock.lock_id AND tlock.expire>NOW()) '
-            .' LEFT JOIN '.TASK_TABLE.' task
-                ON ( task.object_id = ticket.ticket_id AND task.object_type="T" ) '
-            .' LEFT JOIN '.THREAD_TABLE.' thread
-                ON ( thread.object_id = ticket.ticket_id AND thread.object_type="T" ) '
-            .' LEFT JOIN '.THREAD_ENTRY_TABLE.' entry
-                ON ( entry.thread_id = thread.id ) '
-            .' LEFT JOIN '.ATTACHMENT_TABLE.' attach
-                ON ( attach.object_id = entry.id AND attach.`type` = "H") '
-            .' WHERE ticket.ticket_id='.db_input($id)
-            .' GROUP BY ticket.ticket_id';
-
-        //echo $sql;
-        if (!($res=db_query($sql)) || !db_num_rows($res))
-            return false;
-
-
-        $this->ht = db_fetch_array($res);
-
-        $this->id       = $this->ht['ticket_id'];
-        $this->number   = $this->ht['number'];
-        $this->_answers = array();
+    var $owner;     // TicketOwner
+    var $_user;      // EndUser
+    var $_answers;
+    var $collaborators;
+    var $active_collaborators;
+    var $recipients;
+    var $lastrespondent;
 
+    function __onload() {
         $this->loadDynamicData();
-
-        //Reset the sub classes (initiated ondemand)...good for reloads.
-        $this->status= null;
-        $this->staff = null;
-        $this->client = null;
-        $this->team  = null;
-        $this->dept = null;
-        $this->sla = null;
-        $this->tlock = null;
-        $this->stats = null;
-        $this->topic = null;
-        $this->thread = null;
-        $this->collaborators = null;
-
-        return true;
     }
 
     function loadDynamicData() {
-        if (!$this->_answers) {
+        if (!isset($this->_answers)) {
+            $this->_answers = array();
             foreach (DynamicFormEntry::forTicket($this->getId(), true) as $form) {
                 foreach ($form->getAnswers() as $answer) {
                     $tag = mb_strtolower($answer->field->name)
@@ -303,12 +250,8 @@ implements RestrictedAccess, Threadable, TemplateVariable {
         return $this->_answers;
     }
 
-    function reload() {
-        return $this->load();
-    }
-
     function hasState($state) {
-        return  (strcasecmp($this->getState(), $state)==0);
+        return  strcasecmp($this->getState(), $state) == 0;
     }
 
     function isOpen() {
@@ -316,7 +259,7 @@ implements RestrictedAccess, Threadable, TemplateVariable {
     }
 
     function isReopened() {
-        return ($this->getReopenDate());
+        return null !== $this->getReopenDate();
     }
 
     function isReopenable() {
@@ -336,15 +279,15 @@ implements RestrictedAccess, Threadable, TemplateVariable {
     }
 
     function isAssigned() {
-        return ($this->isOpen() && ($this->getStaffId() || $this->getTeamId()));
+        return $this->isOpen() && ($this->getStaffId() || $this->getTeamId());
     }
 
     function isOverdue() {
-        return ($this->ht['isoverdue']);
+        return $this->ht['isoverdue'];
     }
 
     function isAnswered() {
-       return ($this->ht['isanswered']);
+       return $this->ht['isanswered'];
     }
 
     function isLocked() {
@@ -352,19 +295,20 @@ implements RestrictedAccess, Threadable, TemplateVariable {
     }
 
     function checkStaffPerm($staff, $perm=null) {
-
         // Must be a valid staff
         if (!$staff instanceof Staff && !($staff=Staff::lookup($staff)))
             return false;
 
         // Check access based on department or assignment
-        if (!(!$staff->showAssignedOnly()
-                    && $staff->canAccessDept($this->getDeptId()))
-                // only open tickets can be considered assigned
-                && $this->isOpen()
-                && $staff->getId() != $this->getStaffId()
-                && !$staff->isTeamMember($this->getTeamId()))
+        if (($staff->showAssignedOnly()
+            || !$staff->canAccessDept($this->getDeptId()))
+            // only open tickets can be considered assigned
+            && $this->isOpen()
+            && $staff->getId() != $this->getStaffId()
+            && !$staff->isTeamMember($this->getTeamId())
+        ) {
             return false;
+        }
 
         // At this point staff has view access unless a specific permission is
         // requested
@@ -380,55 +324,50 @@ implements RestrictedAccess, Threadable, TemplateVariable {
     }
 
     function checkUserAccess($user) {
-
         if (!$user || !($user instanceof EndUser))
             return false;
 
-        //Ticket Owner
+        // Ticket Owner
         if ($user->getId() == $this->getUserId())
             return true;
 
-        //Collaborator?
+        // Collaborator?
         // 1) If the user was authorized via this ticket.
         if ($user->getTicketId() == $this->getId()
-                && !strcasecmp($user->getUserType(), 'collaborator'))
+            && !strcasecmp($user->getUserType(), 'collaborator')
+        ) {
             return true;
-
+        }
         // 2) Query the database to check for expanded access...
         if (Collaborator::lookup(array(
-                        'user_id' => $user->getId(),
-                        'thread_id' => $this->getThreadId())))
+            'user_id' => $user->getId(),
+            'thread_id' => $this->getThreadId()))
+        ) {
             return true;
-
+        }
         return false;
     }
 
-    //Getters
-    function getId() {
-        return  $this->id;
-    }
-
+    // Getters
     function getNumber() {
         return $this->number;
     }
 
     function getOwnerId() {
-        return $this->ht['user_id'];
+        return $this->user_id;
     }
 
     function getOwner() {
-
-        if (!isset($this->owner)
-                && ($u=User::lookup($this->getOwnerId())))
-            $this->owner = new TicketOwner(new EndUser($u), $this);
-
+        if (!isset($this->owner)) {
+            $this->owner = new TicketOwner(new EndUser($this->user), $this);
+        }
         return $this->owner;
     }
 
-    function getEmail(){
-        if ($o = $this->getOwner())
+    function getEmail() {
+        if ($o = $this->getOwner()) {
             return $o->getEmail();
-
+        }
         return null;
     }
 
@@ -444,8 +383,9 @@ implements RestrictedAccess, Threadable, TemplateVariable {
     }
 
     function getName(){
-        if ($o = $this->getOwner())
+        if ($o = $this->getOwner()) {
             return $o->getName();
+        }
         return null;
     }
 
@@ -455,15 +395,12 @@ implements RestrictedAccess, Threadable, TemplateVariable {
 
     /* Help topic title  - NOT object -> $topic */
     function getHelpTopic() {
-
-        if(!$this->ht['helptopic'] && ($topic=$this->getTopic()))
-            $this->ht['helptopic'] = $topic->getFullName();
-
-        return $this->ht['helptopic'];
+        if ($this->topic)
+            return $this->topic->getFullName();
     }
 
     function getCreateDate() {
-        return $this->ht['created'];
+        return $this->created;
     }
 
     function getOpenDate() {
@@ -471,19 +408,19 @@ implements RestrictedAccess, Threadable, TemplateVariable {
     }
 
     function getReopenDate() {
-        return $this->ht['reopened'];
+        return $this->reopened;
     }
 
     function getUpdateDate() {
-        return $this->ht['updated'];
+        return $this->updated;
     }
 
     function getEffectiveDate() {
-        return $this->ht['lastupdate'];
+        return $this->lastupdate;
     }
 
     function getDueDate() {
-        return $this->ht['duedate'];
+        return $this->duedate;
     }
 
     function getSLADueDate() {
@@ -497,30 +434,25 @@ implements RestrictedAccess, Threadable, TemplateVariable {
     }
 
     function updateEstDueDate() {
-        $estimatedDueDate = $this->getEstDueDate();
-        if ($estimatedDueDate != $this->ht['est_duedate']) {
-            $sql = 'UPDATE '.TICKET_TABLE.' SET `est_duedate`='.db_input($estimatedDueDate)
-                .' WHERE `ticket_id`='.db_input($this->getId());
-            db_query($sql);
-        }
+        $this->est_duedate = $this->getEstDueDate();
+        $this->save();
     }
 
     function getEstDueDate() {
-
-        //Real due date
-        if(($duedate=$this->getDueDate()))
+        // Real due date
+        if ($duedate = $this->getDueDate()) {
             return $duedate;
-
-        //return sla due date (If ANY)
+        }
+        // return sla due date (If ANY)
         return $this->getSLADueDate();
     }
 
     function getCloseDate() {
-        return $this->ht['closed'];
+        return $this->closed;
     }
 
     function getStatusId() {
-        return $this->ht['status_id'];
+        return $this->status_id;
     }
 
     /**
@@ -532,47 +464,38 @@ implements RestrictedAccess, Threadable, TemplateVariable {
     // XXX: Use ::setStatus to change the status. This can be used as a
     //      fallback if the logic in ::setStatus fails.
     function setStatusId($id) {
-        $sql = 'UPDATE '.TICKET_TABLE.' SET updated=NOW() '.
-               ' ,status_id='.db_input($id) .
-               ' WHERE ticket_id='.db_input($this->getId());
-
-        return (db_query($sql) && db_affected_rows());
+        $this->status_id = $id;
+        return $this->save();
     }
 
     function getStatus() {
-
-        if (!$this->status && $this->getStatusId())
-            $this->status = TicketStatus::lookup($this->getStatusId());
-
         return $this->status;
     }
 
     function getState() {
-
-        if (!$this->getStatus())
+        if (!$this->getStatus()) {
             return '';
-
+        }
         return $this->getStatus()->getState();
     }
 
     function getDeptId() {
-       return $this->ht['dept_id'];
+       return $this->dept_id;
     }
 
     function getDeptName() {
-
-        if(!$this->ht['dept_name'] && ($dept = $this->getDept()))
-            $this->ht['dept_name'] = $dept->getFullName();
-
-       return $this->ht['dept_name'];
+        if ($this->dept instanceof Dept)
+            return $this->dept->getFullName();
     }
 
     function getPriorityId() {
         global $cfg;
 
         if (($a = $this->_answers['priority'])
-                && ($b = $a->getValue()))
+            && ($b = $a->getValue())
+        ) {
             return $b->getId();
+        }
         return $cfg->getDefaultPriorityId();
     }
 
@@ -587,11 +510,11 @@ implements RestrictedAccess, Threadable, TemplateVariable {
     }
 
     function getSource() {
-        return $this->ht['source'];
+        return $this->source;
     }
 
     function getIP() {
-        return $this->ht['ip_address'];
+        return $this->ip_address;
     }
 
     function getHashtable() {
@@ -599,36 +522,30 @@ implements RestrictedAccess, Threadable, TemplateVariable {
     }
 
     function getUpdateInfo() {
-        global $cfg;
-
-        $info=array('source'    =>  $this->getSource(),
-                    'topicId'   =>  $this->getTopicId(),
-                    'slaId' =>  $this->getSLAId(),
-                    'user_id' => $this->getOwnerId(),
-                    'duedate'   =>  $this->getDueDate()
-                        ? Format::date($this->getDueDate())
-                        :'',
-                    'time'  =>  $this->getDueDate()?(Format::date($this->getDueDate(), true, 'HH:mm')):'',
-                    );
-
-        return $info;
+        return array(
+            'source'    => $this->getSource(),
+            'topicId'   => $this->getTopicId(),
+            'slaId'     => $this->getSLAId(),
+            'user_id'   => $this->getOwnerId(),
+            'duedate'   => $this->getDueDate()
+                ? Format::date($this->getDueDate())
+                : '',
+            'time'      => $this->getDueDate()?(Format::date($this->getDueDate(), true, 'HH:mm')):'',
+        );
     }
 
     function getLock() {
-        if (!isset($this->tlock) && $this->ht['lock_id'])
-            $this->tlock = Lock::lookup($this->ht['lock_id']);
-
-        return $this->tlock;
+        return $this->lock;
     }
 
     function acquireLock($staffId, $lockTime) {
 
-        if(!$staffId or !$lockTime) //Lockig disabled?
+        if (!$staffId or !$lockTime) //Lockig disabled?
             return null;
 
-        //Check if the ticket is already locked.
-        if(($lock=$this->getLock()) && !$lock->isExpired()) {
-            if($lock->getStaffId()!=$staffId) //someone else locked the ticket.
+        // Check if the ticket is already locked.
+        if (($lock = $this->getLock()) && !$lock->isExpired()) {
+            if ($lock->getStaffId() != $staffId) //someone else locked the ticket.
                 return null;
 
             //Lock already exits...renew it
@@ -636,18 +553,15 @@ implements RestrictedAccess, Threadable, TemplateVariable {
 
             return $lock;
         }
-        //No lock on the ticket or it is expired
-        $this->tlock = Lock::acquire($staffId, $lockTime); //Create a new lock..
+        // No lock on the ticket or it is expired
+        $this->lock = Lock::acquire($staffId, $lockTime); //Create a new lock..
 
-        if ($this->tlock) {
-            $sql = 'UPDATE '.TICKET_TABLE.' SET `lock_id` = '
-                .db_input($this->tlock->getId())
-                .' WHERE `ticket_id` = '. db_input($this->getId());
-            db_query($sql);
+        if ($this->lock) {
+            $this->save();
         }
 
-        //load and return the newly created lock if any!
-        return $this->tlock;
+        // load and return the newly created lock if any!
+        return $this->lock;
     }
 
     function releaseLock($staffId=false) {
@@ -660,19 +574,14 @@ implements RestrictedAccess, Threadable, TemplateVariable {
         if (!$lock->delete())
             return false;
 
-        $sql = 'UPDATE '.TICKET_TABLE.' SET `lock_id` = 0 WHERE `ticket_id` = '
-            . db_input($this->getId());
-        return ($res = db_query($sql)) && db_affected_rows($res);
+        $this->lock = null;
+        return $this->save();
     }
 
     function getDept() {
         global $cfg;
 
-        if(!$this->dept)
-            if(!($this->dept = Dept::lookup($this->getDeptId())))
-                $this->dept = $cfg->getDefaultDept();
-
-        return $this->dept;
+        return $this->dept ?: $cfg->getDefaultDept();
     }
 
     function getUserId() {
@@ -680,43 +589,34 @@ implements RestrictedAccess, Threadable, TemplateVariable {
     }
 
     function getUser() {
-
-        if(!isset($this->user) && $this->getOwner())
-            $this->user = new EndUser($this->getOwner());
-
-        return $this->user;
+        if (!isset($this->_user) && $this->user) {
+            $this->_user = new EndUser($this->user);
+        }
+        return $this->_user;
     }
 
     function getStaffId() {
-        return $this->ht['staff_id'];
+        return $this->staff_id;
     }
 
     function getStaff() {
-
-        if(!$this->staff && $this->getStaffId())
-            $this->staff= Staff::lookup($this->getStaffId());
-
         return $this->staff;
     }
 
     function getTeamId() {
-        return $this->ht['team_id'];
+        return $this->team_id;
     }
 
     function getTeam() {
-
-        if(!$this->team && $this->getTeamId())
-            $this->team = Team::lookup($this->getTeamId());
-
         return $this->team;
     }
 
     function getAssignee() {
 
-        if($staff=$this->getStaff())
+        if ($staff=$this->getStaff())
             return $staff->getName();
 
-        if($team=$this->getTeam())
+        if ($team=$this->getTeam())
             return $team->getName();
 
         return '';
@@ -724,11 +624,11 @@ implements RestrictedAccess, Threadable, TemplateVariable {
 
     function getAssignees() {
 
-        $assignees=array();
-        if($staff=$this->getStaff())
+        $assignees = array();
+        if ($staff = $this->getStaff())
             $assignees[] = $staff->getName();
 
-        if($team=$this->getTeam())
+        if ($team = $this->getTeam())
             $assignees[] = $team->getName();
 
         return $assignees;
@@ -736,60 +636,47 @@ implements RestrictedAccess, Threadable, TemplateVariable {
 
     function getAssigned($glue='/') {
         $assignees = $this->getAssignees();
-        return $assignees?implode($glue, $assignees):'';
+        return $assignees ? implode($glue, $assignees) : '';
     }
 
     function getTopicId() {
-        return $this->ht['topic_id'];
+        return $this->topic_id;
     }
 
     function getTopic() {
-
-        if(!$this->topic && $this->getTopicId())
-            $this->topic = Topic::lookup($this->getTopicId());
-
         return $this->topic;
     }
 
 
     function getSLAId() {
-        return $this->ht['sla_id'];
+        return $this->sla_id;
     }
 
     function getSLA() {
-
-        if(!$this->sla && $this->getSLAId())
-            $this->sla = SLA::lookup($this->getSLAId());
-
         return $this->sla;
     }
 
     function getLastRespondent() {
 
         if (!isset($this->lastrespondent)) {
-
-            $sql ='SELECT resp.staff_id '
-                 .' FROM '.THREAD_ENTRY_TABLE.' resp '
-                 .' LEFT JOIN '.THREAD_TABLE.' t ON( t.id=resp.thread_id) '
-                 .' LEFT JOIN '.STAFF_TABLE. ' s ON(s.staff_id=resp.staff_id) '
-                 .' WHERE  t.object_id='.db_input($this->getId())
-                 .'     AND t.object_type="T" AND resp.staff_id>0 AND  resp.`type`="R" '
-                 .' ORDER BY resp.created DESC LIMIT 1';
-
-            if(!($res=db_query($sql)) || !db_num_rows($res))
-                return null;
-
-            list($id)=db_fetch_row($res);
-
-            $this->lastrespondent = Staff::lookup($id);
+            $this->lastresponent = Staff::objects()
+                ->filter(array(
+                'staff_id' => static::objects()
+                    ->filter(array(
+                        'thread__entry__type' => 'R',
+                        'thread__entry__staff_id__gt' => 0
+                    ))
+                    ->values_flat('thread__entry__staff_id')
+                    ->order_by('-thread__entry__id')
+                    ->first()
+                ))
+                ->first();
         }
-
         return $this->lastrespondent;
-
     }
 
     function getLastMessageDate() {
-        return $this->ht['lastmessage'];
+        return $this->lastmessage;
     }
 
     function getLastMsgDate() {
@@ -797,20 +684,18 @@ implements RestrictedAccess, Threadable, TemplateVariable {
     }
 
     function getLastResponseDate() {
-        return $this->ht['lastresponse'];
+        return $this->lastresponse;
     }
 
     function getLastRespDate() {
         return $this->getLastResponseDate();
     }
 
-
     function getLastMsgId() {
         return $this->lastMsgId;
     }
 
     function getLastMessage() {
-
         if (!isset($this->last_message)) {
             if ($this->getLastMsgId())
                 $this->last_message = MessageThreadEntry::lookup(
@@ -819,23 +704,20 @@ implements RestrictedAccess, Threadable, TemplateVariable {
             if (!$this->last_message)
                 $this->last_message = $this->getThread()->getLastMessage();
         }
-
         return $this->last_message;
     }
 
     function getNumTasks() {
-        return $this->ht['tasks'];
+        // FIXME: Implement this after merging Tasks
+        return count($this->tasks);
     }
 
     function getThreadId() {
-        return $this->ht['thread_id'];
+        if ($this->thread)
+            return $this->thread->id;
     }
 
     function getThread() {
-
-        if (!$this->thread && $this->getThreadId())
-            $this->thread = TicketThread::lookup($this->getThreadId());
-
         return $this->thread;
     }
 
@@ -885,7 +767,6 @@ implements RestrictedAccess, Threadable, TemplateVariable {
 
     //UserList of recipients  (owner + collaborators)
     function getRecipients() {
-
         if (!isset($this->recipients)) {
             $list = new UserList();
             $list->add($this->getOwner());
@@ -895,7 +776,6 @@ implements RestrictedAccess, Threadable, TemplateVariable {
             }
             $this->recipients = $list;
         }
-
         return $this->recipients;
     }
 
@@ -931,7 +811,7 @@ implements RestrictedAccess, Threadable, TemplateVariable {
 
     function addCollaborator($user, $vars, &$errors) {
 
-        if (!$user || $user->getId()==$this->getOwnerId())
+        if (!$user || $user->getId() == $this->getOwnerId())
             return null;
 
         $vars = array_merge(array(
@@ -989,7 +869,7 @@ implements RestrictedAccess, Threadable, TemplateVariable {
             ));
         }
 
-        unset($this->ht['active_collaborators']);
+        unset($this->active_collaborators);
         $this->collaborators = null;
 
         return true;
@@ -1051,46 +931,29 @@ implements RestrictedAccess, Threadable, TemplateVariable {
 
     //DeptId can NOT be 0. No orphans please!
     function setDeptId($deptId) {
-
-        //Make sure it's a valid department//
-        if(!($dept=Dept::lookup($deptId)) || $dept->getId()==$this->getDeptId())
+        // Make sure it's a valid department
+        if ($deptId == $this->getDeptId() || !($dept=Dept::lookup($deptId))) {
             return false;
-
-
-        $sql='UPDATE '.TICKET_TABLE.' SET updated=NOW(), dept_id='.db_input($deptId)
-            .' WHERE ticket_id='.db_input($this->getId());
-
-        return (db_query($sql) && db_affected_rows());
+        }
+        $this->dept = $dept;
+        return $this->save();
     }
 
-    //Set staff ID...assign/unassign/release (id can be 0)
+    // Set staff ID...assign/unassign/release (id can be 0)
     function setStaffId($staffId) {
-
-        if(!is_numeric($staffId)) return false;
-
-        $sql='UPDATE '.TICKET_TABLE.' SET updated=NOW(), staff_id='.db_input($staffId)
-            .' WHERE ticket_id='.db_input($this->getId());
-
-        if (!db_query($sql)  || !db_affected_rows())
+        if (!is_numeric($staffId))
             return false;
 
-        $this->staff = null;
-        $this->ht['staff_id'] = $staffId;
-
-        return true;
+        $this->staff = Staff::lookup($staffId);
+        return $this->save();
     }
 
     function setSLAId($slaId) {
-        if ($slaId == $this->getSLAId()) return true;
-        $rv = db_query(
-             'UPDATE '.TICKET_TABLE.' SET sla_id='.db_input($slaId)
-            .' WHERE ticket_id='.db_input($this->getId()))
-            && db_affected_rows();
-        if ($rv) {
-            $this->ht['sla_id'] = $slaId;
-            $this->sla = null;
-        }
-        return $rv;
+        if ($slaId == $this->getSLAId())
+            return true;
+
+        $this->sla = Sla::lookup($slaId);
+        return $this->save();
     }
     /**
      * Selects the appropriate service-level-agreement plan for this ticket.
@@ -1125,13 +988,11 @@ implements RestrictedAccess, Threadable, TemplateVariable {
 
     //Set team ID...assign/unassign/release (id can be 0)
     function setTeamId($teamId) {
+        if (!is_numeric($teamId))
+            return false;
 
-        if(!is_numeric($teamId)) return false;
-
-        $sql='UPDATE '.TICKET_TABLE.' SET updated=NOW(), team_id='.db_input($teamId)
-            .' WHERE ticket_id='.db_input($this->getId());
-
-        return (db_query($sql)  && db_affected_rows());
+        $this->team = Team::lookup($teamId);
+        return $this->save();
     }
 
     //Status helper.
@@ -1139,7 +1000,7 @@ implements RestrictedAccess, Threadable, TemplateVariable {
     function setStatus($status, $comments='', &$errors=array(), $set_closing_agent=true) {
         global $thisstaff;
 
-        if ($thisstaff && !($role=$thisstaff->getRole($this->getDeptId())))
+        if ($thisstaff && !($role = $thisstaff->getRole($this->getDeptId())))
             return false;
 
         if ($status && is_numeric($status))
@@ -1169,8 +1030,7 @@ implements RestrictedAccess, Threadable, TemplateVariable {
         if ($this->getStatusId() == $status->getId())
             return true;
 
-        $sql = 'UPDATE '.TICKET_TABLE.' SET updated=NOW() '.
-               ' ,status_id='.db_input($status->getId());
+        $this->status = $status;
 
         //TODO: move this up.
         $ecb = null;
@@ -1182,13 +1042,13 @@ implements RestrictedAccess, Threadable, TemplateVariable {
                     '', '');
                     return false;
                 }
-                $sql.=', closed=NOW(), lastupdate=NOW(), duedate=NULL ';
+                $this->closed = $this->lastupdate = SqlFunction::NOW();
+                $this->duedate = null;
                 if ($thisstaff && $set_closing_agent)
-                    $sql.=', staff_id='.db_input($thisstaff->getId());
+                    $this->staff = $thisstaff;
                 $this->clearOverdue();
 
                 $ecb = function($t) {
-                    $t->reload();
                     $t->logEvent('closed');
                     $t->deleteDrafts();
                 };
@@ -1196,7 +1056,7 @@ implements RestrictedAccess, Threadable, TemplateVariable {
             case 'open':
                 // TODO: check current status if it allows for reopening
                 if ($this->isClosed()) {
-                    $sql .= ',closed=NULL, lastupdate=NOW(), reopened=NOW() ';
+                    $this->closed = $this->lastupdate = $this->reopened = SqlFunction::NOW();
                     $ecb = function ($t) {
                         $t->logEvent('reopened', false, 'closed');
                     };
@@ -1204,16 +1064,14 @@ implements RestrictedAccess, Threadable, TemplateVariable {
 
                 // If the ticket is not open then clear answered flag
                 if (!$this->isOpen())
-                    $sql .= ', isanswered = 0 ';
+                    $this->isanswered = 0;
                 break;
             default:
                 return false;
 
         }
 
-        $sql.=' WHERE ticket_id='.db_input($this->getId());
-
-        if (!db_query($sql) || !db_affected_rows())
+        if (!$this->save())
             return false;
 
         // Log status change b4 reload — if currently has a status. (On new
@@ -1244,30 +1102,23 @@ implements RestrictedAccess, Threadable, TemplateVariable {
     }
 
     function setState($state, $alerts=false) {
-
-        switch(strtolower($state)) {
-            case 'open':
-                return $this->setStatus('open');
-                break;
-            case 'closed':
-                return $this->setStatus('closed');
-                break;
-            case 'answered':
-                return $this->setAnsweredState(1);
-                break;
-            case 'unanswered':
-                return $this->setAnsweredState(0);
-                break;
-            case 'overdue':
-                return $this->markOverdue();
-                break;
-            case 'notdue':
-                return $this->clearOverdue();
-                break;
-            case 'unassined':
-                return $this->unassign();
-        }
-
+        switch (strtolower($state)) {
+        case 'open':
+            return $this->setStatus('open');
+        case 'closed':
+            return $this->setStatus('closed');
+        case 'answered':
+            return $this->setAnsweredState(1);
+        case 'unanswered':
+            return $this->setAnsweredState(0);
+        case 'overdue':
+            return $this->markOverdue();
+        case 'notdue':
+            return $this->clearOverdue();
+        case 'unassined':
+            return $this->unassign();
+        }
+        // FIXME: Throw and excception and add test cases
         return false;
     }
 
@@ -1275,11 +1126,8 @@ implements RestrictedAccess, Threadable, TemplateVariable {
 
 
     function setAnsweredState($isanswered) {
-
-        $sql='UPDATE '.TICKET_TABLE.' SET isanswered='.db_input($isanswered)
-            .' WHERE ticket_id='.db_input($this->getId());
-
-        return (db_query($sql) && db_affected_rows());
+        $this->isanswered = $isanswered;
+        return $this->save();
     }
 
     function reopen() {
@@ -1302,16 +1150,17 @@ implements RestrictedAccess, Threadable, TemplateVariable {
 
         //Log stuff here...
 
-        if(!$autorespond && !$alertstaff) return true; //No alerts to send.
+        if (!$autorespond && !$alertstaff)
+            return true; //No alerts to send.
 
         /* ------ SEND OUT NEW TICKET AUTORESP && ALERTS ----------*/
 
-        $this->reload(); //get the new goodies.
         if(!$cfg
-                || !($dept=$this->getDept())
-                || !($tpl = $dept->getTemplate())
-                || !($email=$dept->getAutoRespEmail())) {
-                return false;  //bail out...missing stuff.
+            || !($dept=$this->getDept())
+            || !($tpl = $dept->getTemplate())
+            || !($email=$dept->getAutoRespEmail())
+        ) {
+            return false;  //bail out...missing stuff.
         }
 
         $options = array();
@@ -1329,68 +1178,73 @@ implements RestrictedAccess, Threadable, TemplateVariable {
         }
 
         //Send auto response - if enabled.
-        if($autorespond
-                && $cfg->autoRespONNewTicket()
-                && $dept->autoRespONNewTicket()
-                &&  ($msg=$tpl->getAutoRespMsgTemplate())) {
-
-            $msg = $this->replaceVars($msg->asArray(),
-                    array('message' => $message,
-                          'recipient' => $this->getOwner(),
-                          'signature' => ($dept && $dept->isPublic())?$dept->getSignature():'')
-                    );
-
+        if ($autorespond
+            && $cfg->autoRespONNewTicket()
+            && $dept->autoRespONNewTicket()
+            && ($msg = $tpl->getAutoRespMsgTemplate())
+        ) {
+            $msg = $this->replaceVars(
+                $msg->asArray(),
+                array('message' => $message,
+                      'recipient' => $this->getOwner(),
+                      'signature' => ($dept && $dept->isPublic())?$dept->getSignature():''
+                )
+            );
             $email->sendAutoReply($this->getOwner(), $msg['subj'], $msg['body'],
                 null, $options);
         }
 
-        //Send alert to out sleepy & idle staff.
+        // Send alert to out sleepy & idle staff.
         if ($alertstaff
-                && $cfg->alertONNewTicket()
-                && ($email=$dept->getAlertEmail())
-                && ($msg=$tpl->getNewTicketAlertMsgTemplate())) {
-
+            && $cfg->alertONNewTicket()
+            && ($email=$dept->getAlertEmail())
+            && ($msg=$tpl->getNewTicketAlertMsgTemplate())
+        ) {
             $msg = $this->replaceVars($msg->asArray(), array('message' => $message));
-
-            $recipients=$sentlist=array();
-            //Exclude the auto responding email just incase it's from staff member.
+            $recipients = $sentlist = array();
+            // Exclude the auto responding email just incase it's from staff member.
             if ($message instanceof ThreadEntry && $message->isAutoReply())
                 $sentlist[] = $this->getEmail();
 
-            //Alert admin??
-            if($cfg->alertAdminONNewTicket()) {
+            // Alert admin??
+            if ($cfg->alertAdminONNewTicket()) {
                 $alert = $this->replaceVars($msg, array('recipient' => 'Admin'));
                 $email->sendAlert($cfg->getAdminEmail(), $alert['subj'], $alert['body'], null, $options);
                 $sentlist[]=$cfg->getAdminEmail();
             }
 
-            //Only alerts dept members if the ticket is NOT assigned.
-            if($cfg->alertDeptMembersONNewTicket() && !$this->isAssigned()) {
-                if(($members=$dept->getMembersForAlerts()))
-                    $recipients=array_merge($recipients, $members);
+            // Only alerts dept members if the ticket is NOT assigned.
+            if ($cfg->alertDeptMembersONNewTicket() && !$this->isAssigned()) {
+                if ($members = $dept->getMembersForAlerts())
+                    $recipients = array_merge($recipients, $members);
             }
 
-            if($cfg->alertDeptManagerONNewTicket() && $dept && ($manager=$dept->getManager()))
-                $recipients[]= $manager;
+            if ($cfg->alertDeptManagerONNewTicket() && $dept && ($manager=$dept->getManager()))
+                $recipients[] = $manager;
 
             // Account manager
             if ($cfg->alertAcctManagerONNewMessage()
-                    && ($org = $this->getOwner()->getOrganization())
-                    && ($acct_manager = $org->getAccountManager())) {
+                && ($org = $this->getOwner()->getOrganization())
+                && ($acct_manager = $org->getAccountManager())
+            ) {
                 if ($acct_manager instanceof Team)
                     $recipients = array_merge($recipients, $acct_manager->getMembers());
                 else
                     $recipients[] = $acct_manager;
             }
 
-            foreach( $recipients as $k=>$staff) {
-                if(!is_object($staff) || !$staff->isAvailable() || in_array($staff->getEmail(), $sentlist)) continue;
+            foreach ($recipients as $k=>$staff) {
+                if (!is_object($staff)
+                    || !$staff->isAvailable()
+                    || in_array($staff->getEmail(), $sentlist)
+                ) {
+                    continue;
+                }
                 $alert = $this->replaceVars($msg, array('recipient' => $staff));
                 $email->sendAlert($staff, $alert['subj'], $alert['body'], null, $options);
                 $sentlist[] = $staff->getEmail();
             }
         }
-
         return true;
     }
 
@@ -1403,24 +1257,26 @@ implements RestrictedAccess, Threadable, TemplateVariable {
         $ost->logWarning(sprintf(_S('Maximum Open Tickets Limit (%s)'),$this->getEmail()),
             $msg);
 
-        if(!$sendNotice || !$cfg->sendOverLimitNotice())
+        if (!$sendNotice || !$cfg->sendOverLimitNotice())
             return true;
 
         //Send notice to user.
-        if(($dept = $this->getDept())
+        if (($dept = $this->getDept())
             && ($tpl=$dept->getTemplate())
             && ($msg=$tpl->getOverlimitMsgTemplate())
-            && ($email=$dept->getAutoRespEmail())) {
-
-            $msg = $this->replaceVars($msg->asArray(),
-                        array('signature' => ($dept && $dept->isPublic())?$dept->getSignature():''));
+            && ($email=$dept->getAutoRespEmail())
+        ) {
+            $msg = $this->replaceVars(
+                $msg->asArray(),
+                array('signature' => ($dept && $dept->isPublic())?$dept->getSignature():'')
+            );
 
             $email->sendAutoReply($this->getOwner(), $msg['subj'], $msg['body']);
         }
 
         $user = $this->getOwner();
 
-        //Alert admin...this might be spammy (no option to disable)...but it is helpful..I think.
+        // Alert admin...this might be spammy (no option to disable)...but it is helpful..I think.
         $alert=sprintf(__('Maximum open tickets reached for %s.'), $this->getEmail())."\n"
               .sprintf(__('Open tickets: %d'), $user->getNumOpenTickets())."\n"
               .sprintf(__('Max allowed: %d'), $cfg->getMaxOpenTickets())
@@ -1432,13 +1288,16 @@ implements RestrictedAccess, Threadable, TemplateVariable {
     }
 
     function onResponse($response, $options=array()) {
-        db_query('UPDATE '.TICKET_TABLE.' SET isanswered=1, lastresponse=NOW(), updated=NOW() WHERE ticket_id='.db_input($this->getId()));
-        $this->reload();
-        $vars = array_merge($options,
-                array(
-                    'activity' => _S('New Response'),
-                    'threadentry' => $response));
+        $this->isanswered = 1;
+        $this->lastresponse = SqlFunction::NOW();
+        $this->save();
 
+        $vars = array_merge($options,
+            array(
+                'activity' => _S('New Response'),
+                'threadentry' => $response
+            )
+        );
         $this->onActivity($vars);
     }
 
@@ -1451,14 +1310,15 @@ implements RestrictedAccess, Threadable, TemplateVariable {
         global $cfg;
 
         if (!$entry instanceof ThreadEntry
-                || !($recipients=$this->getRecipients())
-                || !($dept=$this->getDept())
-                || !($tpl=$dept->getTemplate())
-                || !($msg=$tpl->getActivityNoticeMsgTemplate())
-                || !($email=$dept->getEmail()))
+            || !($recipients=$this->getRecipients())
+            || !($dept=$this->getDept())
+            || !($tpl=$dept->getTemplate())
+            || !($msg=$tpl->getActivityNoticeMsgTemplate())
+            || !($email=$dept->getEmail())
+        ) {
             return;
-
-        //Who posted the entry?
+        }
+        // Who posted the entry?
         $skip = array();
         if ($entry instanceof Message) {
             $poster = $entry->getUser();
@@ -1480,10 +1340,10 @@ implements RestrictedAccess, Threadable, TemplateVariable {
         }
 
         $vars = array_merge($vars, array(
-                    'message' => (string) $entry,
-                    'poster' => $poster ?: _S('A collaborator'),
-                    )
-                );
+            'message' => (string) $entry,
+            'poster' => $poster ?: _S('A collaborator'),
+            )
+        );
 
         $msg = $this->replaceVars($msg->asArray(), $vars);
 
@@ -1499,14 +1359,14 @@ implements RestrictedAccess, Threadable, TemplateVariable {
             $email->send($recipient, $notice['subj'], $notice['body'], $attachments,
                 $options);
         }
-
-        return;
     }
 
     function onMessage($message, $autorespond=true) {
         global $cfg;
 
-        db_query('UPDATE '.TICKET_TABLE.' SET isanswered=0,lastupdate=NOW(),lastmessage=NOW() WHERE ticket_id='.db_input($this->getId()));
+        $this->isanswered = 0;
+        $this->lastupdate = SqlFunction::NOW();
+        $this->save();
 
         // Auto-assign to closing staff or last respondent
         // If the ticket is closed and auto-claim is not enabled then put the
@@ -1550,28 +1410,31 @@ implements RestrictedAccess, Threadable, TemplateVariable {
         elseif ($autorespond && ($dept=$this->getDept()))
             $autorespond=$dept->autoRespONNewMessage();
 
+        if (!$autorespond
+            || !$cfg->autoRespONNewMessage()
+            || !$message
+        ) {
+            return;  //no autoresp or alerts.
+        }
 
-        if(!$autorespond
-                || !$cfg->autoRespONNewMessage()
-                || !$message) return;  //no autoresp or alerts.
-
-        $this->reload();
         $dept = $this->getDept();
         $email = $dept->getAutoRespEmail();
 
-        //If enabled...send confirmation to user. ( New Message AutoResponse)
-        if($email
-                && ($tpl=$dept->getTemplate())
-                && ($msg=$tpl->getNewMessageAutorepMsgTemplate())) {
-
+        // If enabled...send confirmation to user. ( New Message AutoResponse)
+        if ($email
+            && ($tpl=$dept->getTemplate())
+            && ($msg=$tpl->getNewMessageAutorepMsgTemplate())
+        ) {
             $msg = $this->replaceVars($msg->asArray(),
-                            array(
-                                'recipient' => $user,
-                                'signature' => ($dept && $dept->isPublic())?$dept->getSignature():''));
-
+                array(
+                    'recipient' => $user,
+                    'signature' => ($dept && $dept->isPublic())?$dept->getSignature():''
+                )
+            );
             $options = array(
-                'inreplyto'=>$message->getEmailMessageId(),
-                'thread'=>$message);
+                'inreplyto' => $message->getEmailMessageId(),
+                'thread' => $message
+            );
             $email->sendAutoReply($user, $msg['subj'], $msg['body'],
                 null, $options);
         }
@@ -1583,15 +1446,17 @@ implements RestrictedAccess, Threadable, TemplateVariable {
         //TODO: do some shit
 
         if (!$alert // Check if alert is enabled
-                || !$cfg->alertONNewActivity()
-                || !($dept=$this->getDept())
-                || !($email=$cfg->getAlertEmail())
-                || !($tpl = $dept->getTemplate())
-                || !($msg=$tpl->getNoteAlertMsgTemplate()))
+            || !$cfg->alertONNewActivity()
+            || !($dept=$this->getDept())
+            || !($email=$cfg->getAlertEmail())
+            || !($tpl = $dept->getTemplate())
+            || !($msg=$tpl->getNoteAlertMsgTemplate())
+        ) {
             return;
+        }
 
         // Alert recipients
-        $recipients=array();
+        $recipients = array();
 
         //Last respondent.
         if ($cfg->alertLastRespondentONNewActivity())
@@ -1599,7 +1464,6 @@ implements RestrictedAccess, Threadable, TemplateVariable {
 
         // Assigned staff / team
         if ($cfg->alertAssignedONNewActivity()) {
-
             if (isset($vars['assignee'])
                     && $vars['assignee'] instanceof Staff)
                  $recipients[] = $vars['assignee'];
@@ -1640,32 +1504,32 @@ implements RestrictedAccess, Threadable, TemplateVariable {
         $sentlist=array();
         foreach ($recipients as $k=>$staff) {
             if (!is_object($staff)
-                    // Don't bother vacationing staff.
-                    || !$staff->isAvailable()
-                    // No need to alert the poster!
-                    || $staffId == $staff->getId()
-                    // No duplicates.
-                    || isset($sentlist[$staff->getEmail()])
-                    // Make sure staff has access to ticket
-                    || ($isClosed && !$this->checkStaffPerm($staff))
-                    )
+                // Don't bother vacationing staff.
+                || !$staff->isAvailable()
+                // No need to alert the poster!
+                || $staffId == $staff->getId()
+                // No duplicates.
+                || isset($sentlist[$staff->getEmail()])
+                // Make sure staff has access to ticket
+                || ($isClosed && !$this->checkStaffPerm($staff))
+            ) {
                 continue;
+            }
             $alert = $this->replaceVars($msg, array('recipient' => $staff));
             $email->sendAlert($staff, $alert['subj'], $alert['body'], null, $options);
             $sentlist[$staff->getEmail()] = 1;
         }
-
     }
 
     function onAssign($assignee, $comments, $alert=true) {
         global $cfg, $thisstaff;
 
-        if($this->isClosed()) $this->reopen(); //Assigned tickets must be open - otherwise why assign?
+        if ($this->isClosed())
+            $this->reopen(); //Assigned tickets must be open - otherwise why assign?
 
-        //Assignee must be an object of type Staff or Team
-        if(!$assignee || !is_object($assignee)) return false;
-
-        $this->reload();
+        // Assignee must be an object of type Staff or Team
+        if (!$assignee || !is_object($assignee))
+            return false;
 
         $user_comments = (bool) $comments;
         $comments = $comments ?: _S('Ticket assignment');
@@ -1678,17 +1542,20 @@ implements RestrictedAccess, Threadable, TemplateVariable {
                 $comments, $assigner, false);
         }
 
-        //See if we need to send alerts
-        if(!$alert || !$cfg->alertONAssignment()) return true; //No alerts!
+        // See if we need to send alerts
+        if (!$alert || !$cfg->alertONAssignment())
+            return true; //No alerts!
 
         $dept = $this->getDept();
-        if(!$dept
-                || !($tpl = $dept->getTemplate())
-                || !($email = $dept->getAlertEmail()))
+        if (!$dept
+            || !($tpl = $dept->getTemplate())
+            || !($email = $dept->getAlertEmail())
+        ) {
             return true;
+        }
 
-        //recipients
-        $recipients=array();
+        // Recipients
+        $recipients = array();
         if ($assignee instanceof Staff) {
             if ($cfg->alertStaffONAssignment())
                 $recipients[] = $assignee;
@@ -1699,82 +1566,97 @@ implements RestrictedAccess, Threadable, TemplateVariable {
                 $recipients[] = $lead;
         }
 
-        //Get the message template
+        // Get the message template
         if ($recipients
-                && ($msg=$tpl->getAssignedAlertMsgTemplate())) {
-
+            && ($msg=$tpl->getAssignedAlertMsgTemplate())
+        ) {
             $msg = $this->replaceVars($msg->asArray(),
-                        array('comments' => $comments,
-                              'assignee' => $assignee,
-                              'assigner' => $assigner
-                              ));
-
-            //Send the alerts.
-            $sentlist=array();
+                array('comments' => $comments,
+                      'assignee' => $assignee,
+                      'assigner' => $assigner
+                )
+            );
+            // Send the alerts.
+            $sentlist = array();
             $options = array(
                 'inreplyto'=>$note->getEmailMessageId(),
                 'references'=>$note->getEmailReferences(),
                 'thread'=>$note);
-            foreach( $recipients as $k=>$staff) {
-                if(!is_object($staff) || !$staff->isAvailable() || in_array($staff->getEmail(), $sentlist)) continue;
+            foreach ($recipients as $k=>$staff) {
+                if (!is_object($staff)
+                    || !$staff->isAvailable()
+                    || in_array($staff->getEmail(), $sentlist)
+                ) {
+                    continue;
+                }
                 $alert = $this->replaceVars($msg, array('recipient' => $staff));
                 $email->sendAlert($staff, $alert['subj'], $alert['body'], null, $options);
                 $sentlist[] = $staff->getEmail();
             }
         }
-
         return true;
     }
 
    function onOverdue($whine=true, $comments="") {
         global $cfg;
 
-        if($whine && ($sla=$this->getSLA()) && !$sla->alertOnOverdue())
+        if ($whine && ($sla = $this->getSLA()) && !$sla->alertOnOverdue())
             $whine = false;
 
-        //check if we need to send alerts.
-        if(!$whine
-                || !$cfg->alertONOverdueTicket()
-                || !($dept = $this->getDept()))
+        // Check if we need to send alerts.
+        if (!$whine
+            || !$cfg->alertONOverdueTicket()
+            || !($dept = $this->getDept())
+        ) {
             return true;
-
-        //Get the message template
-        if(($tpl = $dept->getTemplate())
-                && ($msg=$tpl->getOverdueAlertMsgTemplate())
-                && ($email = $dept->getAlertEmail())) {
-
+        }
+        // Get the message template
+        if (($tpl = $dept->getTemplate())
+            && ($msg=$tpl->getOverdueAlertMsgTemplate())
+            && ($email = $dept->getAlertEmail())
+        ) {
             $msg = $this->replaceVars($msg->asArray(),
-                array('comments' => $comments));
-
-            //recipients
-            $recipients=array();
-            //Assigned staff or team... if any
-            if($this->isAssigned() && $cfg->alertAssignedONOverdueTicket()) {
-                if($this->getStaffId())
+                array('comments' => $comments)
+            );
+            // Recipients
+            $recipients = array();
+            // Assigned staff or team... if any
+            if ($this->isAssigned() && $cfg->alertAssignedONOverdueTicket()) {
+                if ($this->getStaffId()) {
                     $recipients[]=$this->getStaff();
-                elseif($this->getTeamId() && ($team=$this->getTeam()) && ($members=$team->getMembers()))
+                }
+                elseif ($this->getTeamId()
+                    && ($team = $this->getTeam())
+                    && ($members = $team->getMembers())
+                ) {
                     $recipients=array_merge($recipients, $members);
-            } elseif($cfg->alertDeptMembersONOverdueTicket() && !$this->isAssigned()) {
-                //Only alerts dept members if the ticket is NOT assigned.
+                }
+            }
+            elseif ($cfg->alertDeptMembersONOverdueTicket() && !$this->isAssigned()) {
+                // Only alerts dept members if the ticket is NOT assigned.
                 if ($members = $dept->getMembersForAlerts())
                     $recipients = array_merge($recipients, $members);
             }
-            //Always alert dept manager??
-            if($cfg->alertDeptManagerONOverdueTicket() && $dept && ($manager=$dept->getManager()))
+            // Always alert dept manager??
+            if ($cfg->alertDeptManagerONOverdueTicket()
+                && $dept && ($manager=$dept->getManager())
+            ) {
                 $recipients[]= $manager;
-
-            $sentlist=array();
-            foreach( $recipients as $k=>$staff) {
-                if(!is_object($staff) || !$staff->isAvailable() || in_array($staff->getEmail(), $sentlist)) continue;
+            }
+            $sentlist = array();
+            foreach ($recipients as $k=>$staff) {
+                if (!is_object($staff)
+                    || !$staff->isAvailable()
+                    || in_array($staff->getEmail(), $sentlist)
+                ) {
+                    continue;
+                }
                 $alert = $this->replaceVars($msg, array('recipient' => $staff));
                 $email->sendAlert($staff, $alert['subj'], $alert['body'], null);
                 $sentlist[] = $staff->getEmail();
             }
-
         }
-
         return true;
-
     }
 
     // TemplateVariable interface
@@ -1785,47 +1667,46 @@ implements RestrictedAccess, Threadable, TemplateVariable {
     function getVar($tag) {
         global $cfg;
 
-        if($tag && is_callable(array($this, 'get'.ucfirst($tag))))
+        if ($tag && is_callable(array($this, 'get'.ucfirst($tag))))
             return call_user_func(array($this, 'get'.ucfirst($tag)));
 
         switch(mb_strtolower($tag)) {
-            case 'phone':
-            case 'phone_number':
-                return $this->getPhoneNumber();
-                break;
-            case 'auth_token':
-                return $this->getOldAuthToken();
-                break;
-            case 'client_link':
-                return sprintf('%s/view.php?t=%s',
-                        $cfg->getBaseUrl(), $this->getNumber());
-                break;
-            case 'staff_link':
-                return sprintf('%s/scp/tickets.php?id=%d', $cfg->getBaseUrl(), $this->getId());
-                break;
-            case 'create_date':
-                return new FormattedDate($this->getCreateDate());
-                break;
-             case 'due_date':
-                if ($due = $this->getEstDueDate())
-                    return new FormattedDate($due);
-                break;
-            case 'close_date':
-                if ($this->isClosed())
-                    return new FormattedDate($this->getCloseDate());
-                break;
-            case 'last_update':
-                return new FormattedDate($this->last_update);
-            case 'user':
-                return $this->getOwner();
-            default:
-                if (isset($this->_answers[$tag]))
-                    // The answer object is retrieved here which will
-                    // automatically invoke the toString() method when the
-                    // answer is coerced into text
-                    return $this->_answers[$tag];
+        case 'phone':
+        case 'phone_number':
+            return $this->getPhoneNumber();
+            break;
+        case 'auth_token':
+            return $this->getOldAuthToken();
+            break;
+        case 'client_link':
+            return sprintf('%s/view.php?t=%s',
+                    $cfg->getBaseUrl(), $this->getNumber());
+            break;
+        case 'staff_link':
+            return sprintf('%s/scp/tickets.php?id=%d', $cfg->getBaseUrl(), $this->getId());
+            break;
+        case 'create_date':
+            return new FormattedDate($this->getCreateDate());
+            break;
+         case 'due_date':
+            if ($due = $this->getEstDueDate())
+                return new FormattedDate($due);
+            break;
+        case 'close_date':
+            if ($this->isClosed())
+                return new FormattedDate($this->getCloseDate());
+            break;
+        case 'last_update':
+            return new FormattedDate($this->last_update);
+        case 'user':
+            return $this->getOwner();
+        default:
+            if (isset($this->_answers[$tag]))
+                // The answer object is retrieved here which will
+                // automatically invoke the toString() method when the
+                // answer is coerced into text
+                return $this->_answers[$tag];
         }
-
         return false;
     }
 
@@ -1891,7 +1772,6 @@ implements RestrictedAccess, Threadable, TemplateVariable {
         global $ost;
 
         $vars = array_merge($vars, array('ticket' => $this));
-
         return $ost->replaceTemplateVariables($input, $vars);
     }
 
@@ -1904,16 +1784,13 @@ implements RestrictedAccess, Threadable, TemplateVariable {
     }
 
     function markOverdue($whine=true) {
-
         global $cfg;
 
-        if($this->isOverdue())
+        if ($this->isOverdue())
             return true;
 
-        $sql='UPDATE '.TICKET_TABLE.' SET isoverdue=1, updated=NOW() '
-            .' WHERE ticket_id='.db_input($this->getId());
-
-        if(!db_query($sql) || !db_affected_rows())
+        $this->isoverdue = 1;
+        if (!$this->save())
             return false;
 
         $this->logEvent('overdue');
@@ -1923,30 +1800,26 @@ implements RestrictedAccess, Threadable, TemplateVariable {
     }
 
     function clearOverdue() {
-
-        if(!$this->isOverdue())
+        if (!$this->isOverdue())
             return true;
 
         //NOTE: Previously logged overdue event is NOT annuled.
 
-        $sql='UPDATE '.TICKET_TABLE.' SET isoverdue=0, updated=NOW() ';
+        $this->isoverdue = 0;
 
-        //clear due date if it's in the past
-        if($this->getDueDate() && Misc::db2gmtime($this->getDueDate()) <= Misc::gmtime())
-            $sql.=', duedate=NULL';
+        // clear due date if it's in the past
+        if ($this->getDueDate() && Misc::db2gmtime($this->getDueDate()) <= Misc::gmtime())
+            $this->duedate = null;
 
-        //Clear SLA if est. due date is in the past
-        if($this->getSLADueDate() && Misc::db2gmtime($this->getSLADueDate()) <= Misc::gmtime())
-            $sql.=', sla_id=0 ';
-
-        $sql.=' WHERE ticket_id='.db_input($this->getId());
+        // Clear SLA if est. due date is in the past
+        if ($this->getSLADueDate() && Misc::db2gmtime($this->getSLADueDate()) <= Misc::gmtime())
+            $this->sla = null;
 
-        return (db_query($sql) && db_affected_rows());
+        return $this->save();
     }
 
     //Dept Tranfer...with alert.. done by staff
-    function transfer($deptId, $comments, $alert = true) {
-
+    function transfer($deptId, $comments, $alert=true) {
         global $cfg, $thisstaff;
 
         if (!$this->checkStaffPerm($thisstaff, TicketModel::PERM_TRANSFER))
@@ -1954,25 +1827,26 @@ implements RestrictedAccess, Threadable, TemplateVariable {
 
         $currentDept = $this->getDeptName(); //Current department
 
-        if(!$deptId || !$this->setDeptId($deptId))
+        if (!$deptId || !$this->setDeptId($deptId))
             return false;
 
         // Reopen ticket if closed
-        if($this->isClosed()) $this->reopen();
+        if ($this->isClosed())
+            $this->reopen();
 
-        $this->reload();
         $dept = $this->getDept();
 
         // Set SLA of the new department
-        if(!$this->getSLAId() || $this->getSLA()->isTransient())
+        if (!$this->getSLAId() || $this->getSLA()->isTransient())
             $this->selectSLAId();
 
         // Make sure the new department allows assignment to the
         // currently assigned agent (if any)
         if ($this->isAssigned()
-                && ($staff=$this->getStaff())
-                && $dept->assignMembersOnly()
-                && !$dept->isMember($staff)) {
+            && ($staff=$this->getStaff())
+            && $dept->assignMembersOnly()
+            && !$dept->isMember($staff)
+        ) {
             $this->setStaffId(0);
         }
 
@@ -1992,44 +1866,56 @@ implements RestrictedAccess, Threadable, TemplateVariable {
             return true; //no alerts!!
 
          if (($email = $dept->getAlertEmail())
-                     && ($tpl = $dept->getTemplate())
-                     && ($msg=$tpl->getTransferAlertMsgTemplate())) {
-
+             && ($tpl = $dept->getTemplate())
+             && ($msg=$tpl->getTransferAlertMsgTemplate())
+         ) {
             $msg = $this->replaceVars($msg->asArray(),
                 array('comments' => $comments, 'staff' => $thisstaff));
-            //recipients
-            $recipients=array();
-            //Assigned staff or team... if any
+            // Recipients
+            $recipients = array();
+            // Assigned staff or team... if any
             if($this->isAssigned() && $cfg->alertAssignedONTransfer()) {
                 if($this->getStaffId())
-                    $recipients[]=$this->getStaff();
-                elseif($this->getTeamId() && ($team=$this->getTeam()) && ($members=$team->getMembers()))
+                    $recipients[] = $this->getStaff();
+                elseif ($this->getTeamId()
+                    && ($team=$this->getTeam())
+                    && ($members=$team->getMembers())
+                ) {
                     $recipients = array_merge($recipients, $members);
-            } elseif($cfg->alertDeptMembersONTransfer() && !$this->isAssigned()) {
-                //Only alerts dept members if the ticket is NOT assigned.
-                if(($members=$dept->getMembersForAlerts()))
+                }
+            }
+            elseif ($cfg->alertDeptMembersONTransfer() && !$this->isAssigned()) {
+                // Only alerts dept members if the ticket is NOT assigned.
+                if ($members = $dept->getMembersForAlerts())
                     $recipients = array_merge($recipients, $members);
             }
 
-            //Always alert dept manager??
-            if($cfg->alertDeptManagerONTransfer() && $dept && ($manager=$dept->getManager()))
-                $recipients[]= $manager;
-
-            $sentlist = $options = array();
+            // Always alert dept manager??
+            if ($cfg->alertDeptManagerONTransfer()
+                && $dept
+                && ($manager=$dept->getManager())
+            ) {
+                $recipients[] = $manager;
+            }
+            $sentlist = array();
             if ($note) {
                 $options += array(
                     'inreplyto'=>$note->getEmailMessageId(),
                     'references'=>$note->getEmailReferences(),
                     'thread'=>$note);
             }
-            foreach( $recipients as $k=>$staff) {
-                if(!is_object($staff) || !$staff->isAvailable() || in_array($staff->getEmail(), $sentlist)) continue;
+            foreach ($recipients as $k=>$staff) {
+                if (!is_object($staff)
+                    || !$staff->isAvailable()
+                    || in_array($staff->getEmail(), $sentlist)
+                ) {
+                    continue;
+                }
                 $alert = $this->replaceVars($msg, array('recipient' => $staff));
                 $email->sendAlert($staff, $alert['subj'], $alert['body'], null, $options);
                 $sentlist[] = $staff->getEmail();
             }
          }
-
          return true;
     }
 
@@ -2048,7 +1934,7 @@ implements RestrictedAccess, Threadable, TemplateVariable {
 
     function assignToStaff($staff, $note, $alert=true) {
 
-        if(!is_object($staff) && !($staff=Staff::lookup($staff)))
+        if(!is_object($staff) && !($staff = Staff::lookup($staff)))
             return false;
 
         if (!$staff->isAvailable() || !$this->setStaffId($staff->getId()))
@@ -2069,7 +1955,7 @@ implements RestrictedAccess, Threadable, TemplateVariable {
 
     function assignToTeam($team, $note, $alert=true) {
 
-        if(!is_object($team) && !($team=Team::lookup($team)))
+        if(!is_object($team) && !($team = Team::lookup($team)))
             return false;
 
         if (!$team->isActive() || !$this->setTeamId($team->getId()))
@@ -2077,7 +1963,7 @@ implements RestrictedAccess, Threadable, TemplateVariable {
 
         //Clear - staff if it's a closed ticket
         //  staff_id is overloaded -> assigned to & closed by.
-        if($this->isClosed())
+        if ($this->isClosed())
             $this->setStaffId(0);
 
         $this->onAssign($team, $note, $alert);
@@ -2090,39 +1976,39 @@ implements RestrictedAccess, Threadable, TemplateVariable {
     function assign($assignId, $note, $alert=true) {
         global $thisstaff;
 
-        $rv=0;
-        $id=preg_replace("/[^0-9]/", "", $assignId);
-        if($assignId[0]=='t') {
-            $rv=$this->assignToTeam($id, $note, $alert);
-        } elseif($assignId[0]=='s' || is_numeric($assignId)) {
-            $alert=($alert && $thisstaff && $thisstaff->getId()==$id)?false:$alert; //No alerts on self assigned tickets!!!
-            //We don't care if a team is already assigned to the ticket - staff assignment takes precedence
-            $rv=$this->assignToStaff($id, $note, $alert);
+        $rv = 0;
+        $id = preg_replace("/[^0-9]/", "", $assignId);
+        if ($assignId[0] == 't') {
+            $rv = $this->assignToTeam($id, $note, $alert);
+        }
+        elseif ($assignId[0] == 's' || is_numeric($assignId)) {
+            $alert = ($alert && $thisstaff && $thisstaff->getId() == $id)
+                ? false : $alert; //No alerts on self assigned tickets!!!
+            // We don't care if a team is already assigned to the ticket -
+            // staff assignment takes precedence
+            $rv = $this->assignToStaff($id, $note, $alert);
         }
-
         return $rv;
     }
 
-    //unassign primary assignee
+    // Unassign primary assignee
     function unassign() {
-
-        if(!$this->isAssigned()) //We can't release what is not assigned buddy!
+        // We can't release what is not assigned buddy!
+        if (!$this->isAssigned())
             return true;
 
-        //We can only unassigned OPEN tickets.
-        if($this->isClosed())
+        // We can only unassigned OPEN tickets.
+        if ($this->isClosed())
             return false;
 
-        //Unassign staff (if any)
-        if($this->getStaffId() && !$this->setStaffId(0))
+        // Unassign staff (if any)
+        if ($this->getStaffId() && !$this->setStaffId(0))
             return false;
 
-        //unassign team (if any)
-        if($this->getTeamId() && !$this->setTeamId(0))
+        // Unassign team (if any)
+        if ($this->getTeamId() && !$this->setTeamId(0))
             return false;
 
-        $this->reload();
-
         return true;
     }
 
@@ -2135,20 +2021,18 @@ implements RestrictedAccess, Threadable, TemplateVariable {
         global $thisstaff;
 
         if (!$user
-                || ($user->getId() == $this->getOwnerId())
-                || !($this->checkStaffPerm($thisstaff,
-                        TicketModel::PERM_EDIT)))
+            || ($user->getId() == $this->getOwnerId())
+            || !($this->checkStaffPerm($thisstaff,
+                TicketModel::PERM_EDIT))
+        ) {
             return false;
+        }
 
-        $sql ='UPDATE '.TICKET_TABLE.' SET updated = NOW() '
-            .', user_id = '.db_input($user->getId())
-            .' WHERE ticket_id = '.db_input($this->getId());
-
-        if (!db_query($sql) || !db_affected_rows())
+        $this->user_id = $user->getId();
+        if (!$this->save())
             return false;
 
-        $this->ht['user_id'] = $user->getId();
-        $this->user = null;
+        unset($this->user);
         $this->collaborators = null;
         $this->recipients = null;
 
@@ -2165,32 +2049,33 @@ implements RestrictedAccess, Threadable, TemplateVariable {
         return true;
     }
 
-    //Insert message from client
+    // Insert message from client
     function postMessage($vars, $origin='', $alerts=true) {
         global $cfg;
 
         if ($origin)
             $vars['origin'] = $origin;
-        if(isset($vars['ip']))
+        if (isset($vars['ip']))
             $vars['ip_address'] = $vars['ip'];
-        elseif(!$vars['ip_address'] && $_SERVER['REMOTE_ADDR'])
+        elseif (!$vars['ip_address'] && $_SERVER['REMOTE_ADDR'])
             $vars['ip_address'] = $_SERVER['REMOTE_ADDR'];
 
         $errors = array();
-        if(!($message = $this->getThread()->addMessage($vars, $errors)))
+        if (!($message = $this->getThread()->addMessage($vars, $errors)))
             return null;
 
         $this->setLastMessage($message);
 
-        //Add email recipients as collaborators...
+        // Add email recipients as collaborators...
         if ($vars['recipients']
-                && (strtolower($origin) != 'email' || ($cfg && $cfg->addCollabsViaEmail()))
-                //Only add if we have a matched local address
-                && $vars['to-email-id']) {
+            && (strtolower($origin) != 'email' || ($cfg && $cfg->addCollabsViaEmail()))
+            //Only add if we have a matched local address
+            && $vars['to-email-id']
+        ) {
             //New collaborators added by other collaborators are disable --
             // requires staff approval.
             $info = array(
-                    'isactive' => ($message->getUserId() == $this->getUserId())? 1: 0);
+                'isactive' => ($message->getUserId() == $this->getUserId())? 1: 0);
             $collabs = array();
             foreach ($vars['recipients'] as $recipient) {
                 // Skip virtual delivered-to addresses
@@ -2199,15 +2084,21 @@ implements RestrictedAccess, Threadable, TemplateVariable {
 
                 if (($user=User::fromVars($recipient)))
                     if ($c=$this->addCollaborator($user, $info, $errors))
+                        // FIXME: This feels very unwise — should be a
+                        // string indexed array for future
                         $collabs[] = array((string)$c, $recipient['source']);
             }
-            //TODO: Can collaborators add others?
+            // TODO: Can collaborators add others?
             if ($collabs) {
                 $this->logEvent('collab', array('add' => $collabs));
             }
         }
 
-        if(!$alerts) return $message; //Our work is done...
+        // Set the last message time here
+        $this->lastmessage = SqlFunction::NOW();
+
+        if (!$alerts)
+            return $message; //Our work is done...
 
         // Do not auto-respond to bounces and other auto-replies
         $autorespond = isset($vars['flags'])
@@ -2222,29 +2113,27 @@ implements RestrictedAccess, Threadable, TemplateVariable {
             $this->notifyCollaborators($message, array('signature' => ''));
 
         $dept = $this->getDept();
-
-
         $variables = array(
-                'message' => $message,
-                'poster' => ($vars['poster'] ? $vars['poster'] : $this->getName())
-                );
+            'message' => $message,
+            'poster' => ($vars['poster'] ? $vars['poster'] : $this->getName())
+        );
         $options = array(
-                'inreplyto' => $message->getEmailMessageId(),
-                'references' => $message->getEmailReferences(),
-                'thread'=>$message);
-        //If enabled...send alert to staff (New Message Alert)
-        if($cfg->alertONNewMessage()
-                && ($email = $dept->getAlertEmail())
-                && ($tpl = $dept->getTemplate())
-                && ($msg = $tpl->getNewMessageAlertMsgTemplate())) {
-
+            'inreplyto' => $message->getEmailMessageId(),
+            'references' => $message->getEmailReferences(),
+            'thread'=>$message
+        );
+        // If enabled...send alert to staff (New Message Alert)
+        if ($cfg->alertONNewMessage()
+            && ($email = $dept->getAlertEmail())
+            && ($tpl = $dept->getTemplate())
+            && ($msg = $tpl->getNewMessageAlertMsgTemplate())
+        ) {
             $msg = $this->replaceVars($msg->asArray(), $variables);
-
-            //Build list of recipients and fire the alerts.
-            $recipients=array();
+            // Build list of recipients and fire the alerts.
+            $recipients = array();
             //Last respondent.
-            if($cfg->alertLastRespondentONNewMessage() || $cfg->alertAssignedONNewMessage())
-                $recipients[]=$this->getLastRespondent();
+            if ($cfg->alertLastRespondentONNewMessage() || $cfg->alertAssignedONNewMessage())
+                $recipients[] = $this->getLastRespondent();
 
             //Assigned staff if any...could be the last respondent
             if ($cfg->alertAssignedONNewMessage() && $this->isAssigned()) {
@@ -2254,9 +2143,13 @@ implements RestrictedAccess, Threadable, TemplateVariable {
                     $recipients = array_merge($recipients, $team->getMembers());
             }
 
-            //Dept manager
-            if($cfg->alertDeptManagerONNewMessage() && $dept && ($manager=$dept->getManager()))
+            // Dept manager
+            if ($cfg->alertDeptManagerONNewMessage()
+                && $dept
+                && ($manager = $dept->getManager())
+            ) {
                 $recipients[]=$manager;
+            }
 
             // Account manager
             if ($cfg->alertAcctManagerONNewMessage()
@@ -2268,67 +2161,75 @@ implements RestrictedAccess, Threadable, TemplateVariable {
                     $recipients[] = $acct_manager;
             }
 
-            $sentlist=array(); //I know it sucks...but..it works.
-            foreach( $recipients as $k=>$staff) {
-                if(!$staff || !$staff->getEmail() || !$staff->isAvailable() || in_array($staff->getEmail(), $sentlist)) continue;
+            $sentlist = array(); //I know it sucks...but..it works.
+            foreach ($recipients as $k=>$staff) {
+                if (!$staff || !$staff->getEmail()
+                    || !$staff->isAvailable()
+                    || in_array($staff->getEmail(), $sentlist)
+                ) {
+                    continue;
+                }
                 $alert = $this->replaceVars($msg, array('recipient' => $staff));
                 $email->sendAlert($staff, $alert['subj'], $alert['body'], null, $options);
                 $sentlist[] = $staff->getEmail();
             }
         }
-
         return $message;
     }
 
     function postCannedReply($canned, $message, $alert=true) {
         global $ost, $cfg;
 
-        if((!is_object($canned) && !($canned=Canned::lookup($canned))) || !$canned->isEnabled())
+        if ((!is_object($canned) && !($canned=Canned::lookup($canned)))
+            || !$canned->isEnabled()
+        ) {
             return false;
-
+        }
         $files = array();
         foreach ($canned->attachments->getAll() as $file)
             $files[] = $file['id'];
 
         if ($cfg->isRichTextEnabled())
             $response = new HtmlThreadEntryBody(
-                    $this->replaceVars($canned->getHtml()));
+                $this->replaceVars($canned->getHtml()));
         else
             $response = new TextThreadEntryBody(
-                    $this->replaceVars($canned->getPlainText()));
+                $this->replaceVars($canned->getPlainText()));
 
         $info = array('msgId' => $message instanceof ThreadEntry ? $message->getId() : 0,
                       'poster' => __('SYSTEM (Canned Reply)'),
                       'response' => $response,
-                      'cannedattachments' => $files);
-
+                      'cannedattachments' => $files
+        );
         $errors = array();
-        if(!($response=$this->postReply($info, $errors, false)))
+        if (!($response=$this->postReply($info, $errors, false)))
             return null;
 
         $this->markUnAnswered();
 
-        if(!$alert) return $response;
+        if (!$alert)
+            return $response;
 
         $dept = $this->getDept();
 
-        if(($email=$dept->getEmail())
-                && ($tpl = $dept->getTemplate())
-                && ($msg=$tpl->getAutoReplyMsgTemplate())) {
-
-            if($dept && $dept->isPublic())
+        if (($email=$dept->getEmail())
+            && ($tpl = $dept->getTemplate())
+            && ($msg=$tpl->getAutoReplyMsgTemplate())
+        ) {
+            if ($dept && $dept->isPublic())
                 $signature=$dept->getSignature();
             else
                 $signature='';
 
             $msg = $this->replaceVars($msg->asArray(),
-                    array(
-                        'response' => $response,
-                        'signature' => $signature,
-                        'recipient' => $this->getOwner(),
-                        ));
-
-            $attachments =($cfg->emailAttachments() && $files)?$response->getAttachments():array();
+                array(
+                    'response' => $response,
+                    'signature' => $signature,
+                    'recipient' => $this->getOwner(),
+                )
+            );
+            $attachments = ($cfg->emailAttachments() && $files)
+                ? $response->getAttachments() : array();
             $options = array(
                 'inreplyto'=>$response->getEmailMessageId(),
                 'references'=>$response->getEmailReferences(),
@@ -2336,7 +2237,6 @@ implements RestrictedAccess, Threadable, TemplateVariable {
             $email->sendAutoReply($this, $msg['subj'], $msg['body'], $attachments,
                 $options);
         }
-
         return $response;
     }
 
@@ -2345,71 +2245,79 @@ implements RestrictedAccess, Threadable, TemplateVariable {
         global $thisstaff, $cfg;
 
 
-        if(!$vars['poster'] && $thisstaff)
+        if (!$vars['poster'] && $thisstaff)
             $vars['poster'] = $thisstaff;
 
-        if(!$vars['staffId'] && $thisstaff)
+        if (!$vars['staffId'] && $thisstaff)
             $vars['staffId'] = $thisstaff->getId();
 
         if (!$vars['ip_address'] && $_SERVER['REMOTE_ADDR'])
             $vars['ip_address'] = $_SERVER['REMOTE_ADDR'];
 
-        if(!($response = $this->getThread()->addResponse($vars, $errors)))
+        if (!($response = $this->getThread()->addResponse($vars, $errors)))
             return null;
 
         $assignee = $this->getStaff();
         // Set status - if checked.
         if ($vars['reply_status_id']
-                && $vars['reply_status_id'] != $this->getStatusId())
+            && $vars['reply_status_id'] != $this->getStatusId()
+        ) {
             $this->setStatus($vars['reply_status_id']);
+        }
 
         // Claim on response bypasses the department assignment restrictions
-        if($thisstaff && $this->isOpen() && !$this->getStaffId()
-                && $cfg->autoClaimTickets())
+        if ($thisstaff && $this->isOpen() && !$this->getStaffId()
+            && $cfg->autoClaimTickets()
+        ) {
             $this->setStaffId($thisstaff->getId()); //direct assignment;
+        }
 
-        $this->lastrespondent = null;
+        $this->lastrespondent = null; // XXX: Set to $response->staff?
 
         $this->onResponse($response, array('assignee' => $assignee)); //do house cleaning..
 
         /* email the user??  - if disabled - then bail out */
-        if (!$alert) return $response;
+        if (!$alert)
+            return $response;
 
         $dept = $this->getDept();
 
-        if($thisstaff && $vars['signature']=='mine')
+        if ($thisstaff && $vars['signature']=='mine')
             $signature=$thisstaff->getSignature();
-        elseif($vars['signature']=='dept' && $dept && $dept->isPublic())
+        elseif ($vars['signature']=='dept' && $dept && $dept->isPublic())
             $signature=$dept->getSignature();
         else
             $signature='';
 
         $variables = array(
-                'response' => $response,
-                'signature' => $signature,
-                'staff' => $thisstaff,
-                'poster' => $thisstaff);
+            'response' => $response,
+            'signature' => $signature,
+            'staff' => $thisstaff,
+            'poster' => $thisstaff
+        );
         $options = array(
-                'inreplyto' => $response->getEmailMessageId(),
-                'references' => $response->getEmailReferences(),
-                'thread'=>$response);
-
-        if(($email=$dept->getEmail())
-                && ($tpl = $dept->getTemplate())
-                && ($msg=$tpl->getReplyMsgTemplate())) {
+            'inreplyto' => $response->getEmailMessageId(),
+            'references' => $response->getEmailReferences(),
+            'thread'=>$response
+        );
 
+        if (($email=$dept->getEmail())
+            && ($tpl = $dept->getTemplate())
+            && ($msg=$tpl->getReplyMsgTemplate())
+        ) {
             $msg = $this->replaceVars($msg->asArray(),
-                    $variables + array('recipient' => $this->getOwner()));
-
+                $variables + array('recipient' => $this->getOwner())
+            );
             $attachments = $cfg->emailAttachments()?$response->getAttachments():array();
             $email->send($this->getOwner(), $msg['subj'], $msg['body'], $attachments,
                 $options);
         }
 
-        if($vars['emailcollab'])
+        if ($vars['emailcollab']) {
             $this->notifyCollaborators($response,
-                    array('signature' => $signature));
-
+                array('signature' => $signature)
+            );
+        }
         return $response;
     }
 
@@ -2425,20 +2333,19 @@ implements RestrictedAccess, Threadable, TemplateVariable {
 
     //Insert Internal Notes
     function logNote($title, $note, $poster='SYSTEM', $alert=true) {
-
-        $errors = array();
-        //Unless specified otherwise, assume HTML
+        // Unless specified otherwise, assume HTML
         if ($note && is_string($note))
             $note = new HtmlThreadEntryBody($note);
 
+        $errors = array();
         return $this->postNote(
-                array(
-                    'title' => $title,
-                    'note' => $note,
-                ),
-                $errors,
-                $poster,
-                $alert
+            array(
+                'title' => $title,
+                'note' => $note,
+            ),
+            $errors,
+            $poster,
+            $alert
         );
     }
 
@@ -2447,7 +2354,7 @@ implements RestrictedAccess, Threadable, TemplateVariable {
 
         //Who is posting the note - staff or system?
         $vars['staffId'] = 0;
-        if($poster && is_object($poster)) {
+        if ($poster && is_object($poster)) {
             $vars['staffId'] = $poster->getId();
             $vars['poster'] = $poster->getName();
         }
@@ -2460,7 +2367,7 @@ implements RestrictedAccess, Threadable, TemplateVariable {
         if (!$vars['ip_address'] && $_SERVER['REMOTE_ADDR'])
             $vars['ip_address'] = $_SERVER['REMOTE_ADDR'];
 
-        if(!($note=$this->getThread()->addNote($vars, $errors)))
+        if (!($note=$this->getThread()->addNote($vars, $errors)))
             return null;
 
         $alert = $alert && (
@@ -2474,7 +2381,8 @@ implements RestrictedAccess, Threadable, TemplateVariable {
         $assignee = $this->getStaff();
 
         if ($vars['note_status_id']
-                && ($status=TicketStatus::lookup($vars['note_status_id']))) {
+            && ($status=TicketStatus::lookup($vars['note_status_id']))
+        ) {
             if ($this->setStatus($status))
                 $this->reload();
         }
@@ -2502,7 +2410,7 @@ implements RestrictedAccess, Threadable, TemplateVariable {
         }
     }
 
-    //Print ticket... export the ticket thread as PDF.
+    // Print ticket... export the ticket thread as PDF.
     function pdfExport($psize='Letter', $notes=false) {
         global $thisstaff;
 
@@ -2515,7 +2423,7 @@ implements RestrictedAccess, Threadable, TemplateVariable {
         }
 
         $pdf = new Ticket2PDF($this, $psize, $notes);
-        $name='Ticket-'.$this->getNumber().'.pdf';
+        $name = 'Ticket-'.$this->getNumber().'.pdf';
         Http::download($name, 'application/pdf', $pdf->Output($name, 'S'));
         //Remember what the user selected - for autoselect on the next print.
         $_SESSION['PAPER_SIZE'] = $psize;
@@ -2529,8 +2437,7 @@ implements RestrictedAccess, Threadable, TemplateVariable {
         // Fetch thread prior to removing ticket entry
         $t = $this->getThread();
 
-        $sql = 'DELETE FROM '.TICKET_TABLE.' WHERE ticket_id='.$this->getId().' LIMIT 1';
-        if(!db_query($sql) || !db_affected_rows())
+        if (!parent::delete())
             return false;
 
         $t->delete();
@@ -2540,23 +2447,21 @@ implements RestrictedAccess, Threadable, TemplateVariable {
 
         $this->deleteDrafts();
 
-        $sql = 'DELETE FROM '.TICKET_TABLE.'__cdata WHERE `ticket_id`='
-            .db_input($this->getId());
-        // If the CDATA table doesn't exist, that's not an error
-        db_query($sql, false);
+        if ($this->cdata)
+            $this->cdata->delete();
 
         // Log delete
         $log = sprintf(__('Ticket #%1$s deleted by %2$s'),
-                $this->getNumber(),
-                $thisstaff ? $thisstaff->getName() : __('SYSTEM'));
-
+            $this->getNumber(),
+            $thisstaff ? $thisstaff->getName() : __('SYSTEM')
+        );
         if ($comments)
             $log .= sprintf('<hr>%s', $comments);
 
         $ost->logDebug(
-                sprintf( __('Ticket #%s deleted'), $this->getNumber()),
-                $log);
-
+            sprintf( __('Ticket #%s deleted'), $this->getNumber()),
+            $log
+        );
         return true;
     }
 
@@ -2564,33 +2469,41 @@ implements RestrictedAccess, Threadable, TemplateVariable {
         Draft::deleteForNamespace('ticket.%.' . $this->getId());
     }
 
-    function update($vars, &$errors) {
+    function save($refetch=false) {
+        if ($this->dirty) {
+            $this->updated = SqlFunction::NOW();
+        }
+        return parent::save($this->dirty || $refetch);
+    }
 
+    function update($vars, &$errors) {
         global $cfg, $thisstaff;
 
         if (!$cfg
-                || !($this->checkStaffPerm($thisstaff,
-                        TicketModel::PERM_EDIT)))
+            || !($this->checkStaffPerm($thisstaff,
+                TicketModel::PERM_EDIT))
+        ) {
             return false;
-
-        $fields=array();
+        }
+        $fields = array();
         $fields['topicId']  = array('type'=>'int',      'required'=>1, 'error'=>__('Help topic selection is required'));
         $fields['slaId']    = array('type'=>'int',      'required'=>0, 'error'=>__('Select a valid SLA'));
         $fields['duedate']  = array('type'=>'date',     'required'=>0, 'error'=>__('Invalid date format - must be MM/DD/YY'));
 
         $fields['user_id']  = array('type'=>'int',      'required'=>0, 'error'=>__('Invalid user-id'));
 
-        if(!Validator::process($fields, $vars, $errors) && !$errors['err'])
+        if (!Validator::process($fields, $vars, $errors) && !$errors['err'])
             $errors['err'] = __('Missing or invalid data - check the errors and try again');
 
-        if($vars['duedate']) {
-            if($this->isClosed())
+        if ($vars['duedate']) {
+            if ($this->isClosed())
                 $errors['duedate']=__('Due date can NOT be set on a closed ticket');
-            elseif(!$vars['time'] || strpos($vars['time'],':')===false)
+            elseif (!$vars['time'] || strpos($vars['time'],':') === false)
                 $errors['time']=__('Select a time from the list');
-            elseif(strtotime($vars['duedate'].' '.$vars['time'])===false)
+            elseif (strtotime($vars['duedate'].' '.$vars['time']) === false)
                 $errors['duedate']=__('Invalid due date');
-            elseif(strtotime($vars['duedate'].' '.$vars['time'])<=time())
+            // FIXME: Using time() violates database and user timezone
+            elseif (strtotime($vars['duedate'].' '.$vars['time']) <= time())
                 $errors['duedate']=__('Due date must be in the future');
         }
 
@@ -2611,21 +2524,20 @@ implements RestrictedAccess, Threadable, TemplateVariable {
         if ($errors)
             return false;
 
-        $sql='UPDATE '.TICKET_TABLE.' SET updated=NOW() '
-            .' ,topic_id='.db_input($vars['topicId'])
-            .' ,sla_id='.db_input($vars['slaId'])
-            .' ,source='.db_input($vars['source'])
-            .' ,duedate='.($vars['duedate']?db_input(date('Y-m-d G:i',Misc::dbtime($vars['duedate'].' '.$vars['time']))):'NULL');
+        $this->topic_id = $vars['topic_id'];
+        $this->sla_id = $vars['sla_id'];
+        $this->source = $vars['source'];
+        $this->duedate = $vars['duedate']
+            ? date('Y-m-d G:i',Misc::dbtime($vars['duedate'].' '.$vars['time']))
+            : null;
 
-        if($vars['user_id'])
-            $sql.=', user_id='.db_input($vars['user_id']);
-        if($vars['duedate']) { //We are setting new duedate...
-            $sql.=' ,isoverdue=0';
-        }
-
-        $sql.=' WHERE ticket_id='.db_input($this->getId());
+        if ($vars['user_id'])
+            $this->user_id = $vars['user_id'];
+        if ($vars['duedate'])
+            // We are setting new duedate...
+            $this->isoverdue = 0;
 
-        if(!db_query($sql) || !db_affected_rows())
+        if (!$this->save())
             return false;
 
         if ($vars['note'])
@@ -2652,13 +2564,12 @@ implements RestrictedAccess, Threadable, TemplateVariable {
         if ($changes)
             $this->logEvent('edited', array('fields' => $changes));
 
-        // Reload the ticket so we can do further checking
-        $this->reload();
-
         // Reselect SLA if transient
         if (!$keepSLA
-                && (!$this->getSLA() || $this->getSLA()->isTransient()))
+            && (!$this->getSLA() || $this->getSLA()->isTransient())
+        ) {
             $this->selectSLAId();
+        }
 
         // Update estimated due date in database
         $estimatedDueDate = $this->getEstDueDate();
@@ -2666,9 +2577,9 @@ implements RestrictedAccess, Threadable, TemplateVariable {
 
         // Clear overdue flag if duedate or SLA changes and the ticket is no longer overdue.
         if($this->isOverdue()
-                && (!$estimatedDueDate //Duedate + SLA cleared
-                    || Misc::db2gmtime($estimatedDueDate) > Misc::gmtime() //New due date in the future.
-                    )) {
+            && (!$estimatedDueDate //Duedate + SLA cleared
+                || Misc::db2gmtime($estimatedDueDate) > Misc::gmtime() //New due date in the future.
+        )) {
             $this->clearOverdue();
         }
 
@@ -2676,60 +2587,37 @@ implements RestrictedAccess, Threadable, TemplateVariable {
         return true;
     }
 
-
    /*============== Static functions. Use Ticket::function(params); =============nolint*/
-    function getIdByNumber($number, $email=null) {
+    function getIdByNumber($number, $email=null, $ticket=false) {
 
-        if(!$number)
+        if (!$number)
             return 0;
 
-        $sql ='SELECT ticket.ticket_id FROM '.TICKET_TABLE.' ticket '
-             .' LEFT JOIN '.USER_TABLE.' user ON user.id = ticket.user_id'
-             .' LEFT JOIN '.USER_EMAIL_TABLE.' email ON user.id = email.user_id'
-             .' WHERE ticket.`number`='.db_input($number);
+        $query = static::objects()
+            ->filter(array('number' => $number));
 
-        if($email)
-            $sql .= ' AND email.address = '.db_input($email);
+        if ($email)
+            $query->filter(array('user__emails__address' => $email));
 
-        if(($res=db_query($sql)) && db_num_rows($res))
-            list($id)=db_fetch_row($res);
 
-        return $id;
-    }
-
-
-
-    function lookup($id) { //Assuming local ID is the only lookup used!
-        return ($id
-                && is_numeric($id)
-                && ($ticket= new Ticket($id))
-                && $ticket->getId()==$id)
-            ?$ticket:null;
+        if (!$ticket) {
+            $query = $query->values_flat('ticket_id');
+            if ($row = $query->first())
+                return $row[0];
+        }
+        else {
+            return $query->first();
+        }
     }
 
     function lookupByNumber($number, $email=null) {
-        return self::lookup(self:: getIdByNumber($number, $email));
+        return self::getIdByNumber($number, $email, true);
     }
 
     static function isTicketNumberUnique($number) {
-        return 0 == db_num_rows(db_query(
-            'SELECT ticket_id FROM '.TICKET_TABLE.' WHERE `number`='.db_input($number)));
-    }
-
-    function getIdByMessageId($mid, $email) {
-
-        if(!$mid || !$email)
-            return 0;
-
-        $sql='SELECT ticket.ticket_id FROM '.TICKET_TABLE. ' ticket '.
-             ' LEFT JOIN '.TICKET_THREAD_TABLE.' msg USING(ticket_id) '.
-             ' INNER JOIN '.TICKET_EMAIL_INFO_TABLE.' emsg ON (msg.id = emsg.message_id) '.
-             ' WHERE email_mid='.db_input($mid).' AND email='.db_input($email);
-        $id=0;
-        if(($res=db_query($sql)) && db_num_rows($res))
-            list($id)=db_fetch_row($res);
-
-        return $id;
+        return 0 === static::objects()
+            ->filter(array('number' => $number))
+            ->count();
     }
 
     /* Quick staff's tickets stats */
@@ -3187,28 +3075,28 @@ implements RestrictedAccess, Threadable, TemplateVariable {
 
         //We are ready son...hold on to the rails.
         $number = $topic ? $topic->getNewTicketNumber() : $cfg->getNewTicketNumber();
-        $sql='INSERT INTO '.TICKET_TABLE.' SET created=NOW() '
-            .' ,lastupdate= NOW() '
-            .' ,lastmessage= NOW()'
-            .' ,user_id='.db_input($user->getId())
-            .' ,`number`='.db_input($number)
-            .' ,dept_id='.db_input($deptId)
-            .' ,topic_id='.db_input($topicId)
-            .' ,ip_address='.db_input($ipaddress)
-            .' ,source='.db_input($source);
+        $ticket = parent::create(array(
+            'created' => SqlFunction::NOW(),
+            'lastupdate' => SqlFunction::NOW(),
+            'user' => $user,
+            'dept' => $deptId,
+            'topicId' => $topicId,
+            'ip_address' => $ipaddress,
+            'source' => $source,
+        ));
 
         if (isset($vars['emailId']) && $vars['emailId'])
-            $sql.=', email_id='.db_input($vars['emailId']);
+            $ticket->email_id = $vars['emailId'];
 
         //Make sure the origin is staff - avoid firebug hack!
-        if($vars['duedate'] && !strcasecmp($origin,'staff'))
-             $sql.=' ,duedate='.db_input(date('Y-m-d G:i',Misc::dbtime($vars['duedate'].' '.$vars['time'])));
+        if ($vars['duedate'] && !strcasecmp($origin,'staff'))
+            $ticket->duedate = date('Y-m-d G:i',
+                Misc::dbtime($vars['duedate'].' '.$vars['time']));
 
 
-        if(!db_query($sql)
-                || !($id=db_insert_id())
-                || !($thread=TicketThread::create($id))
-                || !($ticket =Ticket::lookup($id)))
+        if (!$ticket->save())
+            return null;
+        if (!($thread = TicketThread::create($ticket->getId())))
             return null;
 
         /* -------------------- POST CREATE ------------------------ */
@@ -3221,12 +3109,12 @@ implements RestrictedAccess, Threadable, TemplateVariable {
                 $form->setAnswer('subject', $topic->getFullName());
             }
         }
-        $form->setTicketId($id);
+        $form->setTicketId($ticket->getId());
         $form->save();
 
         // Save the form data from the help-topic form, if any
         foreach ($topic_forms as $topic_form) {
-            $topic_form->setTicketId($id);
+            $topic_form->setTicketId($ticket->getId());
             $topic_form->save();
         }
 
@@ -3270,7 +3158,7 @@ implements RestrictedAccess, Threadable, TemplateVariable {
         $ticket->selectSLAId($vars['slaId']);
 
         // Assign ticket to staff or team (new ticket by staff)
-        if($vars['assignId']) {
+        if ($vars['assignId']) {
             $ticket->assign($vars['assignId'], $vars['note']);
         }
         else {
@@ -3309,20 +3197,20 @@ implements RestrictedAccess, Threadable, TemplateVariable {
         if ($autorespond && $message instanceof ThreadEntry && $message->isAutoReply())
             $autorespond = false;
 
-        //post canned auto-response IF any (disables new ticket auto-response).
+        // Post canned auto-response IF any (disables new ticket auto-response).
         if ($vars['cannedResponseId']
             && $ticket->postCannedReply($vars['cannedResponseId'], $message, $autorespond)) {
                 $ticket->markUnAnswered(); //Leave the ticket as unanswred.
                 $autorespond = false;
         }
 
-        //Check department's auto response settings
+        // Check department's auto response settings
         // XXX: Dept. setting doesn't affect canned responses.
-        if($autorespond && $dept && !$dept->autoRespONNewTicket())
+        if ($autorespond && $dept && !$dept->autoRespONNewTicket())
             $autorespond=false;
 
-        //Don't send alerts to staff when the message is a bounce
-        //  this is necessary to avoid possible loop (especially on new ticket)
+        // Don't send alerts to staff when the message is a bounce
+        // this is necessary to avoid possible loop (especially on new ticket)
         if ($alertstaff && $message instanceof ThreadEntry && $message->isBounce())
             $alertstaff = false;
 
@@ -3330,10 +3218,11 @@ implements RestrictedAccess, Threadable, TemplateVariable {
         $ticket->onNewTicket($message, $autorespond, $alertstaff);
 
         /************ check if the user JUST reached the max. open tickets limit **********/
-        if($cfg->getMaxOpenTickets()>0
-                    && ($user=$ticket->getOwner())
-                    && ($user->getNumOpenTickets()==$cfg->getMaxOpenTickets())) {
-            $ticket->onOpenLimit(($autorespond && strcasecmp($origin, 'staff')));
+        if ($cfg->getMaxOpenTickets()>0
+            && ($user=$ticket->getOwner())
+            && ($user->getNumOpenTickets()==$cfg->getMaxOpenTickets())
+        ) {
+            $ticket->onOpenLimit($autorespond && strcasecmp($origin, 'staff'));
         }
 
         /* Start tracking ticket lifecycle events */
@@ -3354,11 +3243,16 @@ implements RestrictedAccess, Threadable, TemplateVariable {
         if (!$thisstaff || !$thisstaff->hasPerm(TicketModel::PERM_CREATE))
             return false;
 
-        if($vars['source'] && !in_array(strtolower($vars['source']),array('email','phone','other')))
-            $errors['source']=sprintf(__('Invalid source given - %s'),Format::htmlchars($vars['source']));
+        if ($vars['source'] && !in_array(
+            strtolower($vars['source']), array('email','phone','other'))
+        ) {
+            $errors['source'] = sprintf(
+                __('Invalid source given - %s'),Format::htmlchars($vars['source'])
+            );
+        }
 
         if (!$vars['uid']) {
-            //Special validation required here
+            // Special validation required here
             if (!$vars['email'] || !Validator::is_email($vars['email']))
                 $errors['email'] = __('Valid email address is required');
 
@@ -3369,15 +3263,14 @@ implements RestrictedAccess, Threadable, TemplateVariable {
         if (!$thisstaff->hasPerm(TicketModel::PERM_ASSIGN))
             unset($vars['assignId']);
 
-        //TODO: Deny action based on selected department.
-
+        // TODO: Deny action based on selected department.
 
         $create_vars = $vars;
         $tform = TicketForm::objects()->one()->getForm($create_vars);
         $create_vars['cannedattachments']
             = $tform->getField('message')->getWidget()->getAttachments()->getClean();
 
-        if(!($ticket=Ticket::create($create_vars, $errors, 'staff', false)))
+        if (!($ticket=Ticket::create($create_vars, $errors, 'staff', false)))
             return false;
 
         $vars['msgId']=$ticket->getLastMsgId();
@@ -3388,7 +3281,6 @@ implements RestrictedAccess, Threadable, TemplateVariable {
         // post response - if any
         $response = null;
         if($vars['response'] && $role->hasPerm(TicketModel::PERM_REPLY)) {
-
             $vars['response'] = $ticket->replaceVars($vars['response']);
             // $vars['cannedatachments'] contains the attachments placed on
             // the response form.
@@ -3405,41 +3297,42 @@ implements RestrictedAccess, Threadable, TemplateVariable {
 
         $ticket->reload();
 
-        if(!$cfg->notifyONNewStaffTicket()
-                || !isset($vars['alertuser'])
-                || !($dept=$ticket->getDept()))
+        if (!$cfg->notifyONNewStaffTicket()
+            || !isset($vars['alertuser'])
+            || !($dept=$ticket->getDept())
+        ) {
             return $ticket; //No alerts.
-
-        //Send Notice to user --- if requested AND enabled!!
-        if(($tpl=$dept->getTemplate())
-                && ($msg=$tpl->getNewTicketNoticeMsgTemplate())
-                && ($email=$dept->getEmail())) {
-
+        }
+        // Send Notice to user --- if requested AND enabled!!
+        if (($tpl=$dept->getTemplate())
+            && ($msg=$tpl->getNewTicketNoticeMsgTemplate())
+            && ($email=$dept->getEmail())
+        ) {
             $message = (string) $ticket->getLastMessage();
-            if($response) {
+            if ($response) {
                 $message .= ($cfg->isRichTextEnabled()) ? "<br><br>" : "\n\n";
                 $message .= $response->getBody();
             }
 
-            if($vars['signature']=='mine')
+            if ($vars['signature']=='mine')
                 $signature=$thisstaff->getSignature();
-            elseif($vars['signature']=='dept' && $dept && $dept->isPublic())
+            elseif ($vars['signature']=='dept' && $dept && $dept->isPublic())
                 $signature=$dept->getSignature();
             else
                 $signature='';
 
-            $attachments =($cfg->emailAttachments() && $response)?$response->getAttachments():array();
+            $attachments = ($cfg->emailAttachments() && $response)
+                ? $response->getAttachments() : array();
 
             $msg = $ticket->replaceVars($msg->asArray(),
-                    array(
-                        'message'   => $message,
-                        'signature' => $signature,
-                        'response'  => ($response) ? $response->getBody() : '',
-                        'recipient' => $ticket->getOwner(), //End user
-                        'staff'     => $thisstaff,
-                        )
-                    );
-
+                array(
+                    'message'   => $message,
+                    'signature' => $signature,
+                    'response'  => ($response) ? $response->getBody() : '',
+                    'recipient' => $ticket->getOwner(), //End user
+                    'staff'     => $thisstaff,
+                )
+            );
             $references = array();
             $message = $ticket->getLastMessage();
             if (isset($message))
@@ -3453,12 +3346,21 @@ implements RestrictedAccess, Threadable, TemplateVariable {
             $email->send($ticket->getOwner(), $msg['subj'], $msg['body'], $attachments,
                 $options);
         }
-
         return $ticket;
-
     }
 
-    function checkOverdue() {
+    static function checkOverdue() {
+        /*
+        $overdue = static::objects()
+            ->filter(array(
+                'isoverdue' => 0,
+                Q::any(array(
+                    Q::all(array(
+                        'reopened__isnull' => true,
+                        'duedate__isnull' => true,
+
+         Punt for now
+         */
 
         $sql='SELECT ticket_id FROM '.TICKET_TABLE.' T1 '
             .' INNER JOIN '.TICKET_STATUS_TABLE.' status
diff --git a/include/class.user.php b/include/class.user.php
index 337eaf9501aba38f68752d0b1cdc7a1e187c773a..655edfd00e4dcb9cd6f6023018a1192ff54c02c6 100644
--- a/include/class.user.php
+++ b/include/class.user.php
@@ -39,7 +39,7 @@ class UserModel extends VerySimpleModel {
     static $meta = array(
         'table' => USER_TABLE,
         'pk' => array('id'),
-        'select_related' => array('account', 'default_email'),
+        'select_related' => array('default_email', 'org', 'account'),
         'joins' => array(
             'emails' => array(
                 'reverse' => 'UserEmailModel.user',