diff --git a/include/class.page.php b/include/class.page.php
index 7c29fab113a9b25406ddea40fb0102c6d40be4e5..641832b66d63a7881213aa017848507e5a9a5da3 100644
--- a/include/class.page.php
+++ b/include/class.page.php
@@ -13,42 +13,25 @@
     vim: expandtab sw=4 ts=4 sts=4:
 **********************************************************************/
 
-class Page {
+class Page extends VerySimpleModel {
+
+    static $meta = array(
+        'table' => PAGE_TABLE,
+        'pk' => array('id'),
+        'ordering' => array('name'),
+        'defer' => array('body'),
+        'joins' => array(
+            'topics' => array(
+                'reverse' => 'Topic.page',
+            ),
+        ),
+    );
 
-    var $id;
-    var $ht;
     var $attachments;
+    var $_local;
 
-    function Page($id, $lang=false) {
-        $this->id=0;
-        $this->ht = array();
-        $this->load($id, $lang);
-    }
-
-    function load($id=0, $lang=false) {
-
-        if(!$id && !($id=$this->getId()))
-            return false;
-
-        $sql='SELECT page.*, count(topic.page_id) as topics '
-            .' FROM '.PAGE_TABLE.' page '
-            .' LEFT JOIN '.TOPIC_TABLE. ' topic ON(topic.page_id=page.id) '
-            . ' WHERE page.content_id='.db_input($id)
-            . ($lang ? ' AND lang='.db_input($lang) : '')
-            .' GROUP By page.id';
-
-        if (!($res=db_query($sql)) || !db_num_rows($res))
-            return false;
-
-        $this->ht = db_fetch_array($res);
-        $this->id = $this->ht['id'];
+    function __onload() {
         $this->attachments = new GenericAttachments($this->id, 'P');
-
-        return true;
-    }
-
-    function reload() {
-        return $this->load();
     }
 
     function getId() {
@@ -56,22 +39,27 @@ class Page {
     }
 
     function getHashtable() {
-        return $this->ht;
+        $base = $this->ht;
+        unset($base['topics']);
+        return $base;
     }
 
     function getType() {
-        return $this->ht['type'];
+        return $this->type;
     }
 
     function getName() {
-        return $this->ht['name'];
+        return $this->name;
     }
     function getLocalName($lang=false) {
         return $this->_getLocal('name', $lang);
     }
+    function getNameAsSlug() {
+        return urlencode(Format::slugify($this->name));
+    }
 
     function getBody() {
-        return $this->ht['body'];
+        return $this->body;
     }
     function getLocalBody($lang=false) {
         return $this->_getLocal('body', $lang);
@@ -104,11 +92,11 @@ class Page {
     }
 
     function getNotes() {
-        return $this->ht['notes'];
+        return $this->notes;
     }
 
     function isActive() {
-        return ($this->ht['isactive']);
+        return ($this->isactive);
     }
 
     function isInUse() {
@@ -120,19 +108,19 @@ class Page {
 
 
     function getCreateDate() {
-        return $this->ht['created'];
+        return $this->created;
     }
 
     function getUpdateDate() {
-        return $this->ht['updated'];
+        return $this->updated;
     }
 
     function getNumTopics() {
-        return $this->ht['topics'];
+        return $this->topics->count();
     }
 
     function getTranslateTag($subtag) {
-        return _H(sprintf('page.%s.%s', $subtag, $this->id));
+        return _H(sprintf('page.%s.%s', $subtag, $this->getId()));
     }
     function getLocal($subtag) {
         $tag = $this->getTranslateTag($subtag);
@@ -140,21 +128,6 @@ class Page {
         return $T != $tag ? $T : $this->ht[$subtag];
     }
 
-    function update($vars, &$errors) {
-
-        if(!$vars['isactive'] && $this->isInUse()) {
-            $errors['err'] = __('A page currently in-use CANNOT be disabled!');
-            $errors['isactive'] = __('Page is in-use!');
-        }
-
-        if($errors || !$this->save($this->getId(), $vars, $errors))
-            return false;
-
-        $this->reload();
-
-        return true;
-    }
-
     function disable() {
 
         if(!$this->isActive())
@@ -164,36 +137,37 @@ class Page {
             return false;
 
 
-        $sql=' UPDATE '.PAGE_TABLE.' SET isactive=0 '
-            .' WHERE id='.db_input($this->getId());
-
-        if(!db_query($sql) || !db_affected_rows())
-            return false;
-
-        $this->reload();
-
-        return true;
+        $this->isactive = 0;
+        return $this->save();
     }
 
     function delete() {
 
-        if($this->isInUse())
+        if ($this->isInUse())
             return false;
 
-        $sql='DELETE FROM '.PAGE_TABLE
-            .' WHERE id='.db_input($this->getId())
-            .' LIMIT 1';
-
-        if(!db_query($sql) || !db_affected_rows())
+        if (!parent::delete())
             return false;
 
-        db_query('UPDATE '.TOPIC_TABLE.' SET page_id=0 WHERE page_id='.db_input($this->getId()));
+        return Topic::objects()
+            ->filter(array('page_id'=>$this->getId()))
+            ->update(array('page_id'=>0));
+    }
 
-        return true;
+    function save($refetch=false) {
+        if ($this->dirty)
+            $this->updated = SqlFunction::NOW();
+        return parent::save($refetch || $this->dirty);
     }
 
     /* ------------------> Static methods <--------------------- */
 
+    static function create($vars=false) {
+        $page = parent::create($vars);
+        $page->created = SqlFunction::NOW();
+        return $page;
+    }
+
     function add($vars, &$errors) {
         if(!($id=self::create($vars, $errors)))
             return false;
@@ -201,79 +175,67 @@ class Page {
         return self::lookup($id);
     }
 
-    function create($vars, &$errors) {
-        return self::save(0, $vars, $errors);
-    }
-
-    function getPages($criteria=array()) {
-
-        $sql = ' SELECT id FROM '.PAGE_TABLE.' WHERE 1';
+    static function getPages($criteria=array()) {
+        $pages = self::objects();
         if(isset($criteria['active']))
-            $sql.=' AND  isactive='.db_input($criteria['active']?1:0);
+            $pages = $pages->filter(array('isactive'=>$criteria['active']));
         if(isset($criteria['type']))
-            $sql.=' AND `type`='.db_input($criteria['type']);
-
-        $sql.=' ORDER BY name';
-
-        $pages = array();
-        if(($res=db_query($sql)) && db_num_rows($res))
-            while(list($id) = db_fetch_row($res))
-                $pages[] = Page::lookup($id);
+            $pages = $pages->filter(array('type'=>$criteria['type']));
 
-        return array_filter($pages);
+        return $pages;
     }
 
-    function getActivePages($criteria=array()) {
+    static function getActivePages($criteria=array()) {
 
         $criteria = array_merge($criteria, array('active'=>true));
 
         return self::getPages($criteria);
     }
 
-    function getActiveThankYouPages() {
+    static function getActiveThankYouPages() {
         return self::getActivePages(array('type' => 'thank-you'));
     }
 
-    function getIdByName($name, $lang=false) {
-
-        $id = 0;
-        $sql = ' SELECT id FROM '.PAGE_TABLE.' WHERE name='.db_input($name);
-        if ($lang)
-            $sql .= ' AND lang='.db_input($lang);
-
-        if(($res=db_query($sql)) && db_num_rows($res))
-            list($id) = db_fetch_row($res);
-
-        return $id;
-    }
-
-    function getIdByType($type, $lang=false) {
-        $id = 0;
-        $sql = ' SELECT id FROM '.PAGE_TABLE.' WHERE `type`='.db_input($type);
-        if ($lang)
-            $sql .= ' AND lang='.db_input($lang);
-
-        if(($res=db_query($sql)) && db_num_rows($res))
-            list($id) = db_fetch_row($res);
-
-        return $id;
+    static function getIdByName($name, $lang=false) {
+        try {
+            $qs = self::objects()->filter(array('name'=>$name))
+                ->values_flat('id');
+            if ($lang)
+                $qs = $qs->filter(array('lang'=>$lang));
+            list($id) = $qs->one();
+            return $id;
+        }
+        catch (DoesNotExist $ex) {
+            return null;
+        }
     }
 
-    function lookup($id) {
-        return ($id
-                && is_numeric($id)
-                && ($p= new Page($id))
-                && $p->getId()==$id)
-            ? $p : null;
+    static function getIdByType($type, $lang=false) {
+        try {
+            $qs = self::objects()->filter(array('type'=>$type))
+                ->values_flat('id');
+            if ($lang)
+                $qs = $qs->filter(array('lang'=>$lang));
+            list($id) = $qs->one();
+            return $id;
+        }
+        catch (DoesNotExist $ex) {
+            return null;
+        }
     }
 
-    function save($id, $vars, &$errors) {
+    function update($vars, &$errors) {
 
         //Cleanup.
         $vars['name']=Format::striptags(trim($vars['name']));
 
         //validate
-        if($id && $id!=$vars['id'])
+        if (isset($this->id) && !$vars['isactive'] && $this->isInUse()) {
+            $errors['err'] = __('A page currently in-use CANNOT be disabled!');
+            $errors['isactive'] = __('Page is in-use!');
+        }
+
+        if (isset($this->id) && $this->getId() != $vars['id'])
             $errors['err'] = __('Internal error. Try again');
 
         if(!$vars['type'])
@@ -281,7 +243,7 @@ class Page {
 
         if(!$vars['name'])
             $errors['name'] = __('Name is required');
-        elseif(($pid=self::getIdByName($vars['name'])) && $pid!=$id)
+        elseif(($pid=self::getIdByName($vars['name'])) && $pid!=$this->getId())
             $errors['name'] = __('Name already exists');
 
         if(!$vars['body'])
@@ -289,37 +251,30 @@ class Page {
 
         if($errors) return false;
 
-        //save
-        $sql=' updated=NOW() '
-            .', `type`='.db_input($vars['type'])
-            .', name='.db_input($vars['name'])
-            .', body='.db_input(Format::sanitize($vars['body']))
-            .', isactive='.db_input($vars['isactive'] ? 1 : 0)
-            .', notes='.db_input(Format::sanitize($vars['notes']));
-
-        if($id) {
-            $sql='UPDATE '.PAGE_TABLE.' SET '.$sql.' WHERE id='.db_input($id);
-            if (db_query($sql))
-                return $this->saveTranslations($vars, $errors);
-
-            $errors['err']=sprintf(__('Unable to update %s.'), __('this site page'));
-
-        } else {
-            $sql='INSERT INTO '.PAGE_TABLE.' SET '.$sql.', created=NOW()';
-            if (!db_query($sql) || !($id=db_insert_id())) {
-                $errors['err']=sprintf(__('Unable to create %s.'), __('this site page'))
-                   .' '.__('Internal error occurred');
-                return false;
+        $this->type = $vars['type'];
+        $this->name = $vars['name'];
+        $this->body = Format::sanitize($vars['body']);
+        $this->isactive = (bool) $vars['isactive'];
+        $this->notes = Format::sanitize($vars['notes']);
+
+        if (!isset($this->id)) {
+            if ($this->save()) {
+                $this->content_id = $this->id;
+                $rv = $this->save();
             }
+        }
+        elseif ($this->save())
+            $rv = $this->saveTranslations($vars, $errors);
 
-            $sql = 'UPDATE '.PAGE_TABLE.' SET `content_id`=`id`'
-                .' WHERE id='.db_input($id);
-            if (!db_query($sql))
-                return false;
+        // Attach inline attachments from the editor
+        $page->attachments->deleteInlines();
+        $this->attachments->upload(
+            Draft::getAttachmentIds($vars['body']), true);
 
-            return $id;
-        }
+        if ($rv)
+            return $rv;
 
+        $errors['err']=sprintf(__('Unable to update %s.'), __('this site page'));
         return false;
     }
 
diff --git a/include/class.topic.php b/include/class.topic.php
index ae84b36ffad113186d14427040260a046c93f1ad..955382cd13370454c92ceca2da3ec41764b7c7fe 100644
--- a/include/class.topic.php
+++ b/include/class.topic.php
@@ -29,6 +29,16 @@ class Topic extends VerySimpleModel {
                     'topic_pid' => 'Topic.topic_id',
                 ),
             ),
+            'faqs' => array(
+                'list' => true,
+                'reverse' => 'FaqTopic.topic'
+            ),
+            'page' => array(
+                'null' => true,
+                'constraint' => array(
+                    'page_id' => 'Page.id',
+                ),
+            ),
         ),
     );
 
@@ -41,7 +51,7 @@ class Topic extends VerySimpleModel {
 
     const FLAG_CUSTOM_NUMBERS = 0x0001;
 
-    static function __oninspect() {
+    function __onload() {
         global $cfg;
 
         // Handle upgrade case where sort has not yet been defined
diff --git a/include/staff/pages.inc.php b/include/staff/pages.inc.php
index 19a307f93e2ec9d30fbb94d448f463c5e2e9b438..a2c8e70ee3b317394b3a275e0d5f77b2dca566f1 100644
--- a/include/staff/pages.inc.php
+++ b/include/staff/pages.inc.php
@@ -1,47 +1,35 @@
 <?php
 if(!defined('OSTADMININC') || !$thisstaff->isAdmin()) die('Access Denied');
 
+$pages = Page::objects()
+    ->filter(array('type__in'=>array('other','landing','thank-you','offline')))
+    ->annotate(array('topics'=>Aggregate::count('topics')));
 $qstr='';
-$sql='SELECT page.id, page.isactive, page.name, page.created, page.updated, '
-     .'page.type, count(topic.topic_id) as topics '
-     .' FROM '.PAGE_TABLE.' page '
-     .' LEFT JOIN '.TOPIC_TABLE.' topic ON(topic.page_id=page.id) ';
-$where = ' WHERE type in ("other","landing","thank-you","offline") ';
 $sortOptions=array(
-        'name'=>'page.name', 'status'=>'page.isactive',
-        'created'=>'page.created', 'updated'=>'page.updated',
-        'type'=>'page.type');
+        'name'=>'name', 'status'=>'isactive',
+        'created'=>'created', 'updated'=>'updated',
+        'type'=>'type');
 
-$orderWays=array('DESC'=>'DESC','ASC'=>'ASC');
+$orderWays=array('DESC'=>'-','ASC'=>'');
 $sort=($_REQUEST['sort'] && $sortOptions[strtolower($_REQUEST['sort'])])?strtolower($_REQUEST['sort']):'name';
 //Sorting options...
 if($sort && $sortOptions[$sort]) {
-    $order_column =$sortOptions[$sort];
+    $pages = $pages->order_by(
+        $orderWays[strtoupper($_REQUEST['order'])] ?: ''
+        . $sortOptions[$sort]);
 }
 
-$order_column=$order_column?$order_column:'page.name';
-
-if($_REQUEST['order'] && $orderWays[strtoupper($_REQUEST['order'])]) {
-    $order=$orderWays[strtoupper($_REQUEST['order'])];
-}
-$order=$order?$order:'ASC';
-
-if($order_column && strpos($order_column,',')){
-    $order_column=str_replace(','," $order,",$order_column);
-}
 $x=$sort.'_sort';
 $$x=' class="'.strtolower($order).'" ';
-$order_by="$order_column $order ";
 
-$total=db_count('SELECT count(*) FROM '.PAGE_TABLE.' page '.$where);
+$total = $pages->count();
 $page=($_GET['p'] && is_numeric($_GET['p']))?$_GET['p']:1;
 $pageNav=new Pagenate($total, $page, PAGE_LIMIT);
 $pageNav->setURL('pages.php',$qstr.'&sort='.urlencode($_REQUEST['sort']).'&order='.urlencode($_REQUEST['order']));
 //Ok..lets roll...create the actual query
 $qstr.='&order='.($order=='DESC'?'ASC':'DESC');
-$query="$sql $where GROUP BY page.id ORDER BY $order_by LIMIT ".$pageNav->getStart().",".$pageNav->getLimit();
-$res=db_query($query);
-if($res && ($num=db_num_rows($res)))
+$pages = $pages->limit($pageNav->getLimit())->offset($pageNav->getStart());
+if ($total)
     $showing=$pageNav->showing()._N('site page','site pages', $num);
 else
     $showing=__('No pages found!');
@@ -75,32 +63,32 @@ else
     <tbody>
     <?php
         $total=0;
+print $pages->getQuery();
+print $pages->count();
         $ids=($errors && is_array($_POST['ids']))?$_POST['ids']:null;
-        if($res && db_num_rows($res)):
-            $defaultPages=$cfg->getDefaultPages();
-            while ($row = db_fetch_array($res)) {
+        $defaultPages=$cfg->getDefaultPages();
+        foreach ($pages as $page) {
                 $sel=false;
                 if($ids && in_array($row['id'], $ids))
                     $sel=true;
-                $inuse = ($row['topics'] || in_array($row['id'], $defaultPages));
+                $inuse = ($page->topics || in_array($page->id, $defaultPages));
                 ?>
-            <tr id="<?php echo $row['id']; ?>">
+            <tr id="<?php echo $page->id; ?>">
                 <td width=7px>
-                  <input type="checkbox" class="ckb" name="ids[]" value="<?php echo $row['id']; ?>"
+                  <input type="checkbox" class="ckb" name="ids[]" value="<?php echo $page->id; ?>"
                             <?php echo $sel?'checked="checked"':''; ?>>
                 </td>
-                <td>&nbsp;<a href="pages.php?id=<?php echo $row['id']; ?>"><?php echo Format::htmlchars($row['name']); ?></a></td>
+                <td>&nbsp;<a href="pages.php?id=<?php echo $page->id; ?>"><?php echo Format::htmlchars($page->getLocalName()); ?></a></td>
                 <td class="faded"><?php echo $row['type']; ?></td>
                 <td>
-                    &nbsp;<?php echo $row['isactive']?__('Active'):'<b>'.__('Disabled').'</b>'; ?>
+                    &nbsp;<?php echo $page->isActive()?__('Active'):'<b>'.__('Disabled').'</b>'; ?>
                     &nbsp;&nbsp;<?php echo $inuse?'<em>'.__('(in-use)').'</em>':''; ?>
                 </td>
-                <td>&nbsp;<?php echo Format::db_date($row['created']); ?></td>
-                <td>&nbsp;<?php echo Format::db_datetime($row['updated']); ?></td>
+                <td>&nbsp;<?php echo Format::db_date($page->created); ?></td>
+                <td>&nbsp;<?php echo Format::db_datetime($page->updated); ?></td>
             </tr>
             <?php
-            } //end of while.
-        endif; ?>
+        } //end of foreach. ?>
     <tfoot>
      <tr>
         <td colspan="6">
diff --git a/scp/pages.php b/scp/pages.php
index f818272afebc2578a8cc97f42698c1c9fb4b634b..773d38fd7dc3366256519d33fbecf8da74b3fad7 100644
--- a/scp/pages.php
+++ b/scp/pages.php
@@ -23,13 +23,11 @@ if($_REQUEST['id'] && !($page=Page::lookup($_REQUEST['id'])))
 if($_POST) {
     switch(strtolower($_POST['do'])) {
         case 'add':
-            if(($pageId=Page::create($_POST, $errors))) {
+            $page = Page::create();
+            if($page->update($_POST, $errors)) {
+                $pageId = $page->getId();
                 $_REQUEST['a'] = null;
                 $msg=sprintf(__('Successfully added %s'), Format::htmlchars($_POST['name']));
-                // Attach inline attachments from the editor
-                if ($page = Page::lookup($pageId))
-                    $page->attachments->upload(
-                        Draft::getAttachmentIds($_POST['body']), true);
                 Draft::deleteForNamespace('page');
             } elseif(!$errors['err'])
                 $errors['err'] = sprintf(__('Unable to add %s. Correct error(s) below and try again.'),
@@ -43,11 +41,6 @@ if($_POST) {
                 $msg=sprintf(__('Successfully updated %s'),
                     __('this site page'));
                 $_REQUEST['a']=null; //Go back to view
-                // Attach inline attachments from the editor
-                $page->attachments->deleteInlines();
-                $page->attachments->upload(
-                    Draft::getAttachmentIds($_POST['body']),
-                    true);
                 Draft::deleteForNamespace('page.'.$page->getId().'%');
             } elseif(!$errors['err'])
                 $errors['err'] = sprintf(__('Unable to update %s. Correct error(s) below and try again.'),
@@ -64,9 +57,10 @@ if($_POST) {
                 $count=count($_POST['ids']);
                 switch(strtolower($_POST['a'])) {
                     case 'enable':
-                        $sql='UPDATE '.PAGE_TABLE.' SET isactive=1 '
-                            .' WHERE id IN ('.implode(',', db_input($_POST['ids'])).')';
-                        if(db_query($sql) && ($num=db_affected_rows())) {
+                        $num = Page::objects()
+                            ->filter(array('id__in'=>$_POST['ids']))
+                            ->update(array('isactive'=>1));
+                        if ($num) {
                             if($num==$count)
                                 $msg = sprintf(__('Successfully enabled %s'),
                                     _N('selected site page', 'selected site pages', $count));
@@ -80,8 +74,8 @@ if($_POST) {
                         break;
                     case 'disable':
                         $i = 0;
-                        foreach($_POST['ids'] as $k=>$v) {
-                            if(($p=Page::lookup($v)) && $p->disable())
+                        foreach (Page::objects()->filter(array('id__in'=>$_POST['ids'])) as $p) {
+                            if ($p->disable())
                                 $i++;
                         }
 
@@ -96,11 +90,9 @@ if($_POST) {
                                 _N('selected site page', 'selected site pages', $count));
                         break;
                     case 'delete':
-                        $i=0;
-                        foreach($_POST['ids'] as $k=>$v) {
-                            if(($p=Page::lookup($v)) && $p->delete())
-                                $i++;
-                        }
+                        $i = Page::objects()
+                            ->filter(array('id__in'=>$_POST['ids']))
+                            ->delete();
 
                         if($i && $i==$count)
                             $msg = sprintf(__('Successfully deleted %s'),