diff --git a/include/ajax.content.php b/include/ajax.content.php
index 3d016f0a92a1560ffde643de5ecbc26e6a747ca5..8cd1d5bfd9958b9304c0fde5e5b3a60a1fef3fc4 100644
--- a/include/ajax.content.php
+++ b/include/ajax.content.php
@@ -174,7 +174,7 @@ class ContentAjaxAPI extends AjaxController {
 
         $vars = array_merge($content->getHashtable(), $_POST);
         $errors = array();
-        if (!$content->save($id, $vars, $errors)) {
+        if (!$content->update($vars, $errors)) {
             if ($errors['err'])
                 Http::response(422, $errors['err']);
             else
diff --git a/include/class.category.php b/include/class.category.php
index 784e2769ceeaf1c3af9eddb8106412d85e2fabc6..085288f1bf23b0dfe6d9eef20d481c02886121b7 100644
--- a/include/class.category.php
+++ b/include/class.category.php
@@ -224,12 +224,11 @@ class Category extends VerySimpleModel {
     /* ------------------> Static methods <--------------------- */
 
     static function findIdByName($name) {
-        $object = self::objects()->filter(array(
+        $row = self::objects()->filter(array(
             'name'=>$name
-        ))->values_flat('category_id')->one();
+        ))->values_flat('category_id')->first();
 
-        if ($object)
-            return $object[0];
+        return ($row) ? $row[0] : null;
     }
 
     static function findByName($name) {
diff --git a/include/class.orm.php b/include/class.orm.php
index afce3ea1552b7abd441f1da667bb4b5a0f9c2607..17dd050357a5e1b55d24083e8cdea1a5aad59606 100644
--- a/include/class.orm.php
+++ b/include/class.orm.php
@@ -18,6 +18,8 @@
 
 class OrmException extends Exception {}
 class OrmConfigurationException extends Exception {}
+// Database fields/tables do not match codebase
+class InconsistentModelException extends OrmException {}
 
 /**
  * Meta information about a model including edges (relationships), table
@@ -86,7 +88,8 @@ class ModelMeta implements ArrayAccess {
             if (!isset($j['list']))
                 $j['list'] = true;
             if (!isset($j['null']))
-                $j['null'] = $info['null'] ?: false;
+                // By default, reverse releationships can be empty lists
+                $j['null'] = true;
         }
         // XXX: Make this better (ie. composite keys)
         $keys = array_keys($j['constraint']);
@@ -2076,8 +2079,8 @@ class MysqlExecutor {
 
     function execute() {
         if (!($this->stmt = db_prepare($this->sql)))
-            throw new OrmException('Unable to prepare query: '.db_error()
-                .' '.$this->sql);
+            throw new InconsistentModelException(
+                'Unable to prepare query: '.db_error().' '.$this->sql);
         if (count($this->params))
             $this->_bind($this->params);
         if (!$this->stmt->execute() || ! $this->stmt->store_result()) {
diff --git a/include/class.page.php b/include/class.page.php
index d4fd340a1708d0097e56ea910dec71fb41629e4e..18f92d64a280a17d4bfe288be9317a34fcdef2bd 100644
--- a/include/class.page.php
+++ b/include/class.page.php
@@ -220,6 +220,11 @@ class Page extends VerySimpleModel {
         catch (DoesNotExist $ex) {
             return null;
         }
+        catch (InconsistentModelException $ex) {
+            // This largely happens on upgrades, and may specifically cause
+            // the staff login box to crash
+            return null;
+        }
     }
 
     static function lookupByType($type, $lang=false) {
diff --git a/include/staff/categories.inc.php b/include/staff/categories.inc.php
index 3708e09ca1a897b42137d0fe5c42c0e48c05c999..32ad0a8e92fa4b978c3f7194b9d35de5eda53b65 100644
--- a/include/staff/categories.inc.php
+++ b/include/staff/categories.inc.php
@@ -26,10 +26,9 @@ $total=$categories->count();
 $page=($_GET['p'] && is_numeric($_GET['p']))?$_GET['p']:1;
 $pageNav=new Pagenate($total, $page, PAGE_LIMIT);
 $pageNav->setURL('categories.php',$qstr.'&sort='.urlencode($_REQUEST['sort']).'&order='.urlencode($_REQUEST['order']));
+$pageNav->paginate($categories);
 $qstr.='&order='.($order=='DESC'?'ASC':'DESC');
 
-$categories = $categories->offset($pageNav->getStart())
-    ->limit($pageNav->getLimit());
 if ($total)
     $showing=$pageNav->showing().' '.__('categories');
 else
diff --git a/include/staff/page.inc.php b/include/staff/page.inc.php
index 35b5f2c50acfe5d6319cc14790887553e492baa7..028e71b9366729a7019611b64cf55c262947ae2e 100644
--- a/include/staff/page.inc.php
+++ b/include/staff/page.inc.php
@@ -19,11 +19,12 @@ if($page && $_REQUEST['a']!='add'){
     $slug = Format::slugify($info['name']);
     $qstr.='&id='.$page->getId();
     $translations = CustomDataTranslation::allTranslations(
-        $page->getTranslateTag('body'), 'article');
+        $page->getTranslateTag('name:body'), 'article');
     foreach ($cfg->getSecondaryLanguages() as $tag) {
         foreach ($translations as $t) {
             if (strcasecmp($t->lang, $tag) === 0) {
-                $info['trans'][$tag] = Format::viewableImages($t->text);
+                $C = $t->getComplex();
+                $info['trans'][$tag] = Format::viewableImages($C['body']);
                 break;
             }
         }
diff --git a/include/staff/tickets.inc.php b/include/staff/tickets.inc.php
index 337aab7465b5a92a8c9d233d7973cc734ee77435..83f8b4e7e94cfff13c1d9196c944090226d56da8 100644
--- a/include/staff/tickets.inc.php
+++ b/include/staff/tickets.inc.php
@@ -110,7 +110,7 @@ $tickets->annotate(array(
 
 // Select pertinent columns
 // ------------------------------------------------------------
-$tickets->values('lock__lock_id', 'staff_id', 'isoverdue', 'team_id', 'ticket_id', 'number', 'cdata__subject', 'user__default_email__address', 'source', 'cdata__:priority__priority_color', 'cdata__:priority__priority_desc', 'status_id', 'status__name', 'status__state', 'dept_id', 'dept__name', 'user__name', 'lastupdate');
+$tickets->values('lock__staff_id', 'staff_id', 'isoverdue', 'team_id', 'ticket_id', 'number', 'cdata__subject', 'user__default_email__address', 'source', 'cdata__:priority__priority_color', 'cdata__:priority__priority_desc', 'status_id', 'status__name', 'status__state', 'dept_id', 'dept__name', 'user__name', 'lastupdate');
 
 // Apply requested quick filter
 
@@ -268,7 +268,7 @@ $_SESSION[':Q:tickets'] = $tickets;
             $total += 1;
                 $tag=$T['staff_id']?'assigned':'openticket';
                 $flag=null;
-                if($T['lock__lock_id'])
+                if($T['lock__staff_id'] && $T['lock__staff_id'] != $thisstaff->getId())
                     $flag='locked';
                 elseif($T['isoverdue'])
                     $flag='overdue';
@@ -289,7 +289,7 @@ $_SESSION[':Q:tickets'] = $tickets;
                 $tid=$T['number'];
                 $subject = Format::truncate($subject_field->display($subject_field->to_php($T['cdata__subject'])),40);
                 $threadcount=$T['thread_count'];
-                if(!strcasecmp($T['status__state'],'open') && !$T['isanswered'] && !$T['lock__lock_id']) {
+                if(!strcasecmp($T['status__state'],'open') && !$T['isanswered'] && !$T['lock__staff_id']) {
                     $tid=sprintf('<b>%s</b>',$tid);
                 }
                 ?>
diff --git a/open.php b/open.php
index 2311bc2bc57759bbcd812cdb75ace610fc504439..90bce8173998f9b67708bc81ec27220276df811a 100644
--- a/open.php
+++ b/open.php
@@ -67,13 +67,18 @@ if ($cfg->isClientLoginRequired()) {
 }
 
 require(CLIENTINC_DIR.'header.inc.php');
-if($ticket
-        && (
-            (($topic = $ticket->getTopic()) && ($page = $topic->getPage()))
-            || ($page = $cfg->getThankYouPage())
-        )) {
+if ($ticket
+    && (
+        (($topic = $ticket->getTopic()) && ($page = $topic->getPage()))
+        || ($page = $cfg->getThankYouPage())
+    )
+) {
     // Thank the user and promise speedy resolution!
-    echo Format::viewableImages($ticket->replaceVars($page->getBody()));
+    echo Format::viewableImages(
+        $ticket->replaceVars(
+            $page->getLocalBody()
+        )
+    );
 }
 else {
     require(CLIENTINC_DIR.'open.inc.php');