diff --git a/assets/default/css/theme.css b/assets/default/css/theme.css
index fa3432c5aeda55594612550fe2183db435009312..1c27dede5e0257f2689723b42cb822c02ba2d689 100644
--- a/assets/default/css/theme.css
+++ b/assets/default/css/theme.css
@@ -489,6 +489,7 @@ body {
     width: 49.7%;
     display: inline-block;
     box-sizing: border-box;
+    vertical-align: top;
 }
 .category-name {
     display: inline-block;
@@ -1014,11 +1015,13 @@ img.sign-in-image {
 }
 .span4 {
     display: inline-block;
-    width: 31%;
+    width: 30.5%;
     margin: 0 1%;
+    vertical-align: top;
 }
 .span8 {
     display: inline-block;
-    width: 65%;
+    width: 64.5%;
     margin: 0 1%;
+    vertical-align: top;
 }
diff --git a/include/class.category.php b/include/class.category.php
index a32d0563d3cafa733ff1bd7ee1440a502517ca10..784e2769ceeaf1c3af9eddb8106412d85e2fabc6 100644
--- a/include/class.category.php
+++ b/include/class.category.php
@@ -27,6 +27,10 @@ class Category extends VerySimpleModel {
         ),
     );
 
+    const VISIBILITY_FEATURED = 2;
+    const VISIBILITY_PUBLIC = 1;
+    const VISIBILITY_PRIVATE = 0;
+
     var $_local;
 
     /* ------------------> Getter methods <--------------------- */
@@ -34,11 +38,24 @@ class Category extends VerySimpleModel {
     function getName() { return $this->name; }
     function getNumFAQs() { return  $this->faqs->count(); }
     function getDescription() { return $this->description; }
+    function getDescriptionWithImages() { return Format::viewableImages($this->description); }
     function getNotes() { return $this->notes; }
     function getCreateDate() { return $this->created; }
     function getUpdateDate() { return $this->updated; }
 
-    function isPublic() { return $this->ispublic; }
+    function isPublic() {
+        return $this->ispublic != self::VISIBILITY_PRIVATE;
+    }
+    function getVisibilityDescription() {
+        switch ($this->ispublic) {
+        case self::VISIBILITY_PRIVATE:
+            return __('Private');
+        case self::VISIBILITY_PUBLIC:
+            return __('Public');
+        case self::VISIBILITY_FEATURED:
+            return __('Featured');
+        }
+    }
     function getHashtable() { return $this->ht; }
 
     // Translation interface ----------------------------------
@@ -50,8 +67,8 @@ class Category extends VerySimpleModel {
         $T = CustomDataTranslation::translate($tag);
         return $T != $tag ? $T : $this->ht[$subtag];
     }
-    function getLocalDescription($lang=false) {
-        return $this->_getLocal('description', $lang);
+    function getLocalDescriptionWithImages($lang=false) {
+        return Format::viewableImages($this->_getLocal('description', $lang));
     }
     function getLocalName($lang=false) {
         return $this->_getLocal('name', $lang);
@@ -223,7 +240,7 @@ class Category extends VerySimpleModel {
 
     static function getFeatured() {
         return self::objects()->filter(array(
-            'ispublic'=>2
+            'ispublic'=>self::VISIBILITY_FEATURED
         ));
     }
 
diff --git a/include/class.faq.php b/include/class.faq.php
index a8d8c512df782d6e07ed08713b5737227af06bd9..3966bec589df6ecf18f8e75d6e6a33e2935d2153 100644
--- a/include/class.faq.php
+++ b/include/class.faq.php
@@ -22,6 +22,7 @@ class FAQ extends VerySimpleModel {
         'pk' => array('faq_id'),
         'ordering' => array('question'),
         'defer' => array('answer'),
+        'select_related'=> array('category'),
         'joins' => array(
             'category' => array(
                 'constraint' => array(
@@ -36,6 +37,12 @@ class FAQ extends VerySimpleModel {
                 'list' => true,
                 'null' => true,
             ),
+            'topics' => array(
+                'constraint' => array(
+                    'faq_id' => 'FaqTopic.faq_id'
+                ),
+                'null' => true,
+            ),
         ),
     );
 
@@ -43,6 +50,10 @@ class FAQ extends VerySimpleModel {
     var $topics;
     var $_local;
 
+    const VISIBILITY_PRIVATE = 0;
+    const VISIBILITY_PUBLIC = 1;
+    const VISIBILITY_FEATURED = 2;
+
     function __onload() {
         if (isset($this->faq_id))
             $this->attachments = new GenericAttachments($this->getId(), 'F');
@@ -50,7 +61,11 @@ class FAQ extends VerySimpleModel {
 
     /* ------------------> Getter methods <--------------------- */
     function getId() { return $this->faq_id; }
-    function getHashtable() { return $this->ht; }
+    function getHashtable() {
+        $base = $this->ht;
+        unset($base['category']);
+        return $base;
+    }
     function getKeywords() { return $this->keywords; }
     function getQuestion() { return $this->question; }
     function getAnswer() { return $this->answer; }
@@ -67,7 +82,20 @@ class FAQ extends VerySimpleModel {
     function getNotes() { return $this->notes; }
     function getNumAttachments() { return $this->attachments->count(); }
 
-    function isPublished() { return (!!$this->ispublished && !!$this->category->ispublic); }
+    function isPublished() {
+        return $this->ispublished != self::VISIBILITY_PRIVATE
+            && $this->category->isPublic();
+    }
+    function getVisibilityDescription() {
+        switch ($this->ispublished) {
+        case self::VISIBILITY_PRIVATE:
+            return __('Internal');
+        case self::VISIBILITY_PUBLIC:
+            return __('Public');
+        case self::VISIBILITY_FEATURED:
+            return __('Featured');
+        }
+    }
 
     function getCreateDate() { return $this->created; }
     function getUpdateDate() { return $this->updated; }
diff --git a/include/staff/categories.inc.php b/include/staff/categories.inc.php
index a948f4976ca123a275e5c3dc0b663db2788e55d7..1cb927395517c996325a8d2b1863014f88d2482c 100644
--- a/include/staff/categories.inc.php
+++ b/include/staff/categories.inc.php
@@ -2,39 +2,35 @@
 if(!defined('OSTSCPINC') || !$thisstaff) die('Access Denied');
 
 $qstr='';
-$sql='SELECT cat.category_id, cat.name, cat.ispublic, cat.updated, count(faq.faq_id) as faqs '.
-     ' FROM '.FAQ_CATEGORY_TABLE.' cat '.
-     ' LEFT JOIN '.FAQ_TABLE.' faq ON (faq.category_id=cat.category_id) ';
-$sql.=' WHERE 1';
-$sortOptions=array('name'=>'cat.name','type'=>'cat.ispublic','faqs'=>'faqs','updated'=>'cat.updated');
-$orderWays=array('DESC'=>'DESC','ASC'=>'ASC');
+$categories = Category::objects()
+    ->annotate(array('faq_count'=>Aggregate::COUNT('faqs')));
+$sortOptions=array('name'=>'name','type'=>'ispublic','faqs'=>'faq_count','updated'=>'updated');
+$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];
 }
-$order_column=$order_column?$order_column:'cat.name';
+$order_column=$order_column ?: 'name';
 
 if($_REQUEST['order'] && $orderWays[strtoupper($_REQUEST['order'])]) {
     $order=$orderWays[strtoupper($_REQUEST['order'])];
 }
-$order=$order?$order:'ASC';
+$order=$order ?: '';
 
-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 '.FAQ_CATEGORY_TABLE.' cat ');
+$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']));
 $qstr.='&order='.($order=='DESC'?'ASC':'DESC');
-$query="$sql GROUP BY cat.category_id ORDER BY $order_by LIMIT ".$pageNav->getStart().",".$pageNav->getLimit();
-$res=db_query($query);
-if($res && ($num=db_num_rows($res)))
+
+$categories = $categories->offset($pageNav->getStart())
+    ->limit($pageNav->getLimit());
+if ($total)
     $showing=$pageNav->showing().' '.__('categories');
 else
     $showing=__('No FAQ categories found!');
@@ -65,29 +61,27 @@ else
     <?php
         $total=0;
         $ids=($errors && is_array($_POST['ids']))?$_POST['ids']:null;
-        if($res && db_num_rows($res)):
-            while ($row = db_fetch_array($res)) {
-                $sel=false;
-                if($ids && in_array($row['category_id'],$ids))
-                    $sel=true;
+        foreach ($categories as $C) {
+            $sel=false;
+            if ($ids && in_array($C->getId(), $ids))
+                $sel=true;
 
-                $faqs=0;
-                if($row['faqs'])
-                    $faqs=sprintf('<a href="faq.php?cid=%d">%d</a>',$row['category_id'],$row['faqs']);
-                ?>
-            <tr id="<?php echo $row['category_id']; ?>">
+            $faqs=0;
+            if ($C->faq_count)
+                $faqs=sprintf('<a href="faq.php?cid=%d">%d</a>',$C->getId(),$C->faq_count);
+            ?>
+            <tr id="<?php echo $C->getId(); ?>">
                 <td width=7px>
-                  <input type="checkbox" name="ids[]" value="<?php echo $row['category_id']; ?>" class="ckb"
+                  <input type="checkbox" name="ids[]" value="<?php echo $C->getId(); ?>" class="ckb"
                             <?php echo $sel?'checked="checked"':''; ?>>
                 </td>
-                <td><a href="categories.php?id=<?php echo $row['category_id']; ?>"><?php echo Format::truncate($row['name'],200); ?></a>&nbsp;</td>
-                <td><?php echo $row['ispublic']?'<b>'.__('Public').'</b>':__('Internal'); ?></td>
+                <td><a class="truncate" style="width:500px" href="categories.php?id=<?php echo $C->getId(); ?>"><?php
+                    echo $C->getLocalName(); ?></a></td>
+                <td><?php echo $C->getVisibilityDescription(); ?></td>
                 <td style="text-align:right;padding-right:25px;"><?php echo $faqs; ?></td>
-                <td>&nbsp;<?php echo Format::db_datetime($row['updated']); ?></td>
-            </tr>
-            <?php
-            } //end of while.
-        endif; ?>
+                <td>&nbsp;<?php echo Format::db_datetime($C->updated); ?></td>
+            </tr><?php
+        } // end of foreach ?>
     <tfoot>
      <tr>
         <td colspan="5">
diff --git a/include/staff/faq-categories.inc.php b/include/staff/faq-categories.inc.php
index 9d9efa32ba994d56730d3b49f8fa62e343455fdb..0019380d022a5e9fd00d7fa87b74785c41854c0b 100644
--- a/include/staff/faq-categories.inc.php
+++ b/include/staff/faq-categories.inc.php
@@ -10,21 +10,19 @@ if(!defined('OSTSTAFFINC') || !$thisstaff) die('Access Denied');
         <select name="cid" id="cid">
             <option value="">&mdash; <?php echo __('All Categories');?> &mdash;</option>
             <?php
-            $sql='SELECT category_id, name, count(faq.category_id) as faqs '
-                .' FROM '.FAQ_CATEGORY_TABLE.' cat '
-                .' LEFT JOIN '.FAQ_TABLE.' faq USING(category_id) '
-                .' GROUP BY cat.category_id '
-                .' HAVING faqs>0 '
-                .' ORDER BY cat.name DESC ';
-            if(($res=db_query($sql)) && db_num_rows($res)) {
-                while($row=db_fetch_array($res))
-                    echo sprintf('<option value="%d" %s>%s (%d)</option>',
-                            $row['category_id'],
-                            ($_REQUEST['cid'] && $row['category_id']==$_REQUEST['cid']?'selected="selected"':''),
-                            $row['name'],
-                            $row['faqs']);
-            }
-            ?>
+            $categories = Category::objects()
+                ->annotate(array('faq_count'=>Aggregate::COUNT('faqs')))
+                ->filter(array('faq_count__gt'=>0))
+                ->order_by('name');
+print $categories;
+            foreach ($categories as $C) {
+                echo sprintf('<option value="%d" %s>%s (%d)</option>',
+                    $C->getId(),
+                    ($_REQUEST['cid'] && $C->getId()==$_REQUEST['cid']?'selected="selected"':''),
+                    $C->getLocalName(),
+                    $C->faq_count
+                );
+            } ?>
         </select>
         <input id="searchSubmit" type="submit" value="<?php echo __('Search');?>">
     </div>
@@ -32,19 +30,18 @@ if(!defined('OSTSTAFFINC') || !$thisstaff) die('Access Denied');
         <select name="topicId" style="width:350px;" id="topic-id">
             <option value="">&mdash; <?php echo __('All Help Topics');?> &mdash;</option>
             <?php
-            $sql='SELECT ht.topic_id, CONCAT_WS(" / ", pht.topic, ht.topic) as helptopic, count(faq.topic_id) as faqs '
-                .' FROM '.TOPIC_TABLE.' ht '
-                .' LEFT JOIN '.TOPIC_TABLE.' pht ON (pht.topic_id=ht.topic_pid) '
-                .' LEFT JOIN '.FAQ_TOPIC_TABLE.' faq ON(faq.topic_id=ht.topic_id) '
-                .' GROUP BY ht.topic_id '
-                .' HAVING faqs>0 '
-                .' ORDER BY helptopic';
-            if(($res=db_query($sql)) && db_num_rows($res)) {
-                while($row=db_fetch_array($res))
-                    echo sprintf('<option value="%d" %s>%s (%d)</option>',
-                            $row['topic_id'],
-                            ($_REQUEST['topicId'] && $row['topic_id']==$_REQUEST['topicId']?'selected="selected"':''),
-                            $row['helptopic'], $row['faqs']);
+            $topics = Topic::objects()
+                ->annotate(array('faq_count'=>Aggregate::COUNT('faqs')))
+                ->filter(array('faq_count__gt'=>0))
+                ->all();
+            usort($topics, function($a, $b) {
+                return strcmp($a->getFullName(), $b->getFullName());
+            });
+            foreach ($topics as $T) {
+                echo sprintf('<option value="%d" %s>%s (%d)</option>',
+                    $T->getId(),
+                    ($_REQUEST['topicId'] && $T->getId()==$_REQUEST['topicId']?'selected="selected"':''),
+                    $T->getFullName(), $T->faq_count);
             }
             ?>
         </select>
@@ -54,39 +51,36 @@ if(!defined('OSTSTAFFINC') || !$thisstaff) die('Access Denied');
 <div>
 <?php
 if($_REQUEST['q'] || $_REQUEST['cid'] || $_REQUEST['topicId']) { //Search.
-    $sql='SELECT faq.faq_id, question, ispublished, count(attach.file_id) as attachments, count(ft.topic_id) as topics '
-        .' FROM '.FAQ_TABLE.' faq '
-        .' LEFT JOIN '.FAQ_CATEGORY_TABLE.' cat ON(cat.category_id=faq.category_id) '
-        .' LEFT JOIN '.FAQ_TOPIC_TABLE.' ft ON(ft.faq_id=faq.faq_id) '
-        .' LEFT JOIN '.ATTACHMENT_TABLE.' attach
-             ON(attach.object_id=faq.faq_id AND attach.type=\'F\' AND attach.inline = 0) '
-        .' WHERE 1 ';
+    $faqs = FAQ::objects()
+        ->annotate(array(
+            'attachment_count'=>Aggregate::COUNT('attachments'),
+            'topic_count'=>Aggregate::COUNT('topics')
+        ))
+        ->order_by('question');
 
-    if($_REQUEST['cid'])
-        $sql.=' AND faq.category_id='.db_input($_REQUEST['cid']);
+    if ($_REQUEST['cid'])
+        $faqs->filter(array('category_id'=>$_REQUEST['cid']));
 
-    if($_REQUEST['topicId'])
-        $sql.=' AND ft.topic_id='.db_input($_REQUEST['topicId']);
-
-    if($_REQUEST['q']) {
-        $sql.=" AND (question LIKE ('%".db_input($_REQUEST['q'],false)."%')
-                 OR answer LIKE ('%".db_input($_REQUEST['q'],false)."%')
-                 OR keywords LIKE ('%".db_input($_REQUEST['q'],false)."%')
-                 OR cat.name LIKE ('%".db_input($_REQUEST['q'],false)."%')
-                 OR cat.description LIKE ('%".db_input($_REQUEST['q'],false)."%')
-                 )";
-    }
+    if ($_REQUEST['topicId'])
+        $faqs->filter(array('topic_id'=>$_REQUEST['topicId']));
 
-    $sql.=' GROUP BY faq.faq_id ORDER BY question';
+    if ($_REQUEST['q'])
+        $faqs->filter(Q::ANY(array(
+            'question__contains'=>$_REQUEST['q'],
+            'answer__contains'=>$_REQUEST['q'],
+            'keywords__contains'=>$_REQUEST['q'],
+            'category__name__contains'=>$_REQUEST['q'],
+            'category__description__contains'=>$_REQUEST['q'],
+        )));
 
     echo "<div><strong>".__('Search Results')."</strong></div><div class='clear'></div>";
-    if(($res=db_query($sql)) && db_num_rows($res)) {
+    if ($faqs->exists(true)) {
         echo '<div id="faq">
                 <ol>';
-        while($row=db_fetch_array($res)) {
-            echo sprintf('
-                <li><a href="faq.php?id=%d" class="previewfaq">%s</a> - <span>%s</span></li>',
-                $row['faq_id'],$row['question'],$row['ispublished']?__('Published'):__('Internal'));
+        foreach ($faqs as $F) {
+            echo sprintf(
+                '<li><a href="faq.php?id=%d" class="previewfaq">%s</a> - <span>%s</span></li>',
+                $F->getId(), $F->getLocalQuestion(), $F->getVisibilityDescription());
         }
         echo '  </ol>
              </div>';
@@ -94,23 +88,25 @@ if($_REQUEST['q'] || $_REQUEST['cid'] || $_REQUEST['topicId']) { //Search.
         echo '<strong class="faded">'.__('The search did not match any FAQs.').'</strong>';
     }
 } else { //Category Listing.
-    $sql='SELECT cat.category_id, cat.name, cat.description, cat.ispublic, count(faq.faq_id) as faqs '
-        .' FROM '.FAQ_CATEGORY_TABLE.' cat '
-        .' LEFT JOIN '.FAQ_TABLE.' faq ON(faq.category_id=cat.category_id) '
-        .' GROUP BY cat.category_id '
-        .' ORDER BY cat.name';
-    if(($res=db_query($sql)) && db_num_rows($res)) {
+    $categories = Category::objects()
+        ->annotate(array('faq_count'=>Aggregate::COUNT('faqs')))
+        ->all();
+
+    if (count($categories)) {
+        usort($categories, function($a, $b) {
+            return strcmp($a->getLocalName(), $b->getLocalName());
+        });
         echo '<div>'.__('Click on the category to browse FAQs or manage its existing FAQs.').'</div>
                 <ul id="kb">';
-        while($row=db_fetch_array($res)) {
-
+        foreach ($categories as $C) {
             echo sprintf('
                 <li>
-                    <h4><a href="kb.php?cid=%d">%s (%d)</a> - <span>%s</span></h4>
+                    <h4><a class="truncate" style="max-width:600px" href="kb.php?cid=%d">%s (%d)</a> - <span>%s</span></h4>
                     %s
-                </li>',$row['category_id'],$row['name'],$row['faqs'],
-                ($row['ispublic']?__('Public'):__('Internal')),
-                Format::safe_html($row['description']));
+                </li>',$C->getId(),$C->getLocalName(),$C->faq_count,
+                $C->getVisibilityDescription(),
+                Format::safe_html($C->getLocalDescriptionWithImages())
+            );
         }
         echo '</ul>';
     } else {
diff --git a/include/staff/faq-view.inc.php b/include/staff/faq-view.inc.php
index 8cb85661cbd2bd9d607156ff0da2760538d98592..0b5533ff8c5a8e1564a3bd11fc103ec916eaed7b 100644
--- a/include/staff/faq-view.inc.php
+++ b/include/staff/faq-view.inc.php
@@ -49,9 +49,9 @@ if ($otherLangs) { ?>
     <div><strong><?php echo __('Other Languages'); ?></strong></div>
 <?php
     foreach ($otherLangs as $lang) { ?>
-    <a href="faq.php?kblang=<?php echo $lang; ?>&id=<?php echo $faq->getId(); ?>">
+    <div><a href="faq.php?kblang=<?php echo $lang; ?>&id=<?php echo $faq->getId(); ?>">
         <?php echo Internationalization::getLanguageDescription($lang); ?>
-    </a>
+    </a></div>
     <?php } ?>
 </section>
 <?php } ?>
diff --git a/include/staff/faq.inc.php b/include/staff/faq.inc.php
index 4578c1f2d71311568fd51111a002aeb2248219da..7315a7fa1667a3fb319b222edb224d2ca02857b3 100644
--- a/include/staff/faq.inc.php
+++ b/include/staff/faq.inc.php
@@ -14,7 +14,7 @@ if($faq){
     $qstr='id='.$faq->getId();
     $langs = $cfg->getSecondaryLanguages();
     $translations = $faq->getAllTranslations();
-    foreach ($cfg->getSecondaryLanguages() as $tag) {
+    foreach ($langs as $tag) {
         foreach ($translations as $t) {
             if (strcasecmp($t->lang, $tag) === 0) {
                 $trans = $t->getComplex();
@@ -102,10 +102,10 @@ if ($topics = Topic::getAllHelpTopics()) {
             <?php echo __('Featured (promote to front page)'); ?>
         </option>
         <option value="1" <?php echo $info['ispublished'] ? 'selected="selected"' : ''; ?>>
-            <?php echo __('Public (publish)'); ?>
+            <?php echo __('Public').' '.__('(publish)'); ?>
         </option>
         <option value="0" <?php echo !$info['ispublished'] ? 'selected="selected"' : ''; ?>>
-            <?php echo __('Internal (private)'); ?>
+            <?php echo __('Internal').' '.('(private)'); ?>
         </option>
     </select>
     <div class="error"><?php echo $errors['ispublished']; ?></div>
@@ -125,6 +125,7 @@ if ($topics = Topic::getAllHelpTopics()) {
 <strong><?php echo __('Knowledgebase Article Content'); ?></strong><br/>
 <?php echo __('Here you can manage the question and answer for the article. Multiple languages are available if enabled in the admin panel.'); ?>
 <div class="clear"></div>
+
 <?php
 $langs = Internationalization::getConfiguredSystemLanguages();
 if ($faq) { ?>
diff --git a/scp/helptopics.php b/scp/helptopics.php
index c447675fda8d796d8fe4528c8e2b77f9a09c3154..01c01337b5f82a083dfd69089c01fc6d5d94fcae 100644
--- a/scp/helptopics.php
+++ b/scp/helptopics.php
@@ -15,6 +15,7 @@
 **********************************************************************/
 require('admin.inc.php');
 include_once(INCLUDE_DIR.'class.topic.php');
+include_once(INCLUDE_DIR.'class.faq.php');
 require_once(INCLUDE_DIR.'class.dynamic_forms.php');
 
 $topic=null;