diff --git a/include/ajax.forms.php b/include/ajax.forms.php
index b6e5d2903b3ea7a7f41afbb3c666a3656a550e05..9a026e83c5861f5621678dcf0864f008efd44e3a 100644
--- a/include/ajax.forms.php
+++ b/include/ajax.forms.php
@@ -14,10 +14,17 @@ class DynamicFormsAjaxAPI extends AjaxController {
     }
 
     function getFormsForHelpTopic($topic_id, $client=false) {
-        $topic = Topic::lookup($topic_id);
-        if ($topic->ht['form_id']
-                && ($form = DynamicForm::lookup($topic->ht['form_id'])))
-            $form->render(!$client);
+        if (!($topic = Topic::lookup($topic_id)))
+            Http::response(404, 'No such help topic');
+
+        if ($_GET || isset($_SESSION[':form-data'])) {
+            if (!is_array($_SESSION[':form-data']))
+                $_SESSION[':form-data'] = array();
+            $_SESSION[':form-data'] = array_merge($_SESSION[':form-data'], $_GET);
+        }
+
+        if ($form = $topic->getForm())
+            $form->getForm($_SESSION[':form-data'])->render(!$client);
     }
 
     function getClientFormsForHelpTopic($topic_id) {
diff --git a/include/class.config.php b/include/class.config.php
index b0373f6e9d9679b2ec93e8b8179efbdf2b2167fc..2cdce1351937f1838b0710e43b72b42574c7c522 100644
--- a/include/class.config.php
+++ b/include/class.config.php
@@ -165,6 +165,8 @@ class OsticketConfig extends Config {
         'clients_only' => false,
         'client_registration' => 'closed',
         'accept_unregistered_email' => true,
+        'default_help_topic' => 0,
+        'help_topic_sort_mode' => 'a',
     );
 
     function OsticketConfig($section=null) {
@@ -426,6 +428,34 @@ class OsticketConfig extends Config {
         return $this->defaultPriority;
     }
 
+    function getDefaultTopicId() {
+        return $this->get('default_help_topic');
+    }
+
+    function getDefaultTopic() {
+        return Topic::lookup($this->getDefaultTopicId());
+    }
+
+    function getTopicSortMode() {
+        return $this->get('help_topic_sort_mode');
+    }
+
+    function setTopicSortMode($mode) {
+        $modes = static::allTopicSortModes();
+        if (!isset($modes[$mode]))
+            throw new InvalidArgumentException($mode
+                .': Unsupport help topic sort mode');
+
+        $this->update('help_topic_sort_mode', $mode);
+    }
+
+    static function allTopicSortModes() {
+        return array(
+            'a' => 'Alphabetically',
+            'm' => 'Manually',
+        );
+    }
+
     function getDefaultTemplateId() {
         return $this->get('default_template_id');
     }
@@ -901,7 +931,7 @@ class OsticketConfig extends Config {
 
         if($vars['enable_captcha']) {
             if (!extension_loaded('gd'))
-                $errors['enable_captcha']='The GD extension required';
+                $errors['enable_captcha']='The GD extension is required';
             elseif(!function_exists('imagepng'))
                 $errors['enable_captcha']='PNG support required for Image Captcha';
         }
@@ -927,7 +957,11 @@ class OsticketConfig extends Config {
                 $errors['max_staff_file_uploads']='Invalid selection. Must be less than '.$maxfileuploads;
         }
 
-
+        if ($vars['default_help_topic']
+                && ($T = Topic::lookup($vars['default_help_topic']))
+                && !$T->isActive()) {
+            $errors['default_help_topic'] = 'Default help topic must be set to active';
+        }
 
         if(!Validator::process($f, $vars, $errors) || $errors)
             return false;
@@ -938,6 +972,7 @@ class OsticketConfig extends Config {
         return $this->updateAll(array(
             'random_ticket_ids'=>$vars['random_ticket_ids'],
             'default_priority_id'=>$vars['default_priority_id'],
+            'default_help_topic'=>$vars['default_help_topic'],
             'default_sla_id'=>$vars['default_sla_id'],
             'max_open_tickets'=>$vars['max_open_tickets'],
             'autolock_minutes'=>$vars['autolock_minutes'],
diff --git a/include/class.email.php b/include/class.email.php
index aa6facef4bdc65069d159e27aaca430160a54c1f..79b307dca3edaebedfcbdb8d63e68c31ed90e7b8 100644
--- a/include/class.email.php
+++ b/include/class.email.php
@@ -86,6 +86,16 @@ class Email {
         return $this->dept;
     }
 
+    function getTopicId() {
+        return $this->ht['topic_id'];
+    }
+
+    function getTopic() {
+        // Topic::lookup will do validation on the ID, no need to duplicate
+        // code here
+        return Topic::lookup($this->getTopicId());
+    }
+
     function autoRespond() {
         return (!$this->ht['noautoresp']);
     }
diff --git a/include/class.ticket.php b/include/class.ticket.php
index c3f49246957ab049d5cc504b7f9e5abb94fb3bd8..b46f151b001ad30366fb5be4686ae694e3d6edb9 100644
--- a/include/class.ticket.php
+++ b/include/class.ticket.php
@@ -248,7 +248,7 @@ class Ticket {
     function getHelpTopic() {
 
         if(!$this->ht['helptopic'] && ($topic=$this->getTopic()))
-            $this->ht['helptopic'] = $topic->getName();
+            $this->ht['helptopic'] = $topic->getFullName();
 
         return $this->ht['helptopic'];
     }
@@ -2403,6 +2403,11 @@ class Ticket {
                 $form->setAnswer('priority', null, $email->getPriorityId());
             if ($autorespond)
                 $autorespond = $email->autoRespond();
+            if (!isset($topic)
+                    && ($T = $email->getTopic())
+                    && ($T->isActive())) {
+                $topic = $T;
+            }
             $email = null;
             $source = 'Email';
         }
@@ -2416,6 +2421,11 @@ class Ticket {
                 $vars['teamId'] = substr($code, 1);
         }
 
+        if (!isset($topic)) {
+            // This may return NULL, no big deal
+            $topic = $cfg->getDefaultTopic();
+        }
+
         // Intenal mapping magic...see if we need to override anything
         if (isset($topic)) {
             $deptId = $deptId ?: $topic->getDeptId();
diff --git a/include/class.topic.php b/include/class.topic.php
index ad3b21a416f147ba443d2417c786f1bb7c6299b2..c415b157aa1e0cf063ae7d912cd2905b1e90dfd9 100644
--- a/include/class.topic.php
+++ b/include/class.topic.php
@@ -23,20 +23,23 @@ class Topic {
     var $page;
     var $form;
 
+    const DISPLAY_DISABLED = 2;
+
+    const FORM_USE_PARENT = 4294967295;
+
     function Topic($id) {
         $this->id=0;
         $this->load($id);
     }
 
     function load($id=0) {
+        global $cfg;
 
         if(!$id && !($id=$this->getId()))
             return false;
 
         $sql='SELECT ht.* '
-            .', IF(ht.topic_pid IS NULL, ht.topic, CONCAT_WS(" / ", ht2.topic, ht.topic)) as name '
             .' FROM '.TOPIC_TABLE.' ht '
-            .' LEFT JOIN '.TOPIC_TABLE.' ht2 ON(ht2.topic_id=ht.topic_pid) '
             .' WHERE ht.topic_id='.db_input($id);
 
         if(!($res=db_query($sql)) || !db_num_rows($res))
@@ -47,6 +50,10 @@ class Topic {
 
         $this->page = $this->form = null;
 
+        // Handle upgrade case where sort has not yet been defined
+        if (!$this->ht['sort'] && $cfg->getTopicSortMode() == 'a') {
+            static::updateSortOrder();
+        }
 
         return true;
     }
@@ -75,7 +82,16 @@ class Topic {
     }
 
     function getName() {
-        return $this->ht['name'];
+        return $this->ht['topic'];
+    }
+
+    function getFullName() {
+        return self::getTopicName($this->getId());
+    }
+
+    static function getTopicName($id) {
+        $names = static::getHelpTopics(false, true);
+        return $names[$id];
     }
 
     function getDeptId() {
@@ -114,8 +130,12 @@ class Topic {
     }
 
     function getForm() {
-        if(!$this->form && $this->getFormId())
-            $this->form = DynamicForm::lookup($this->getFormId());
+        $id = $this->getFormId();
+
+        if ($id == self::FORM_USE_PARENT && ($p = $this->getParent()))
+            $this->form = $p->getForm();
+        elseif ($id && !$this->form)
+            $this->form = DynamicForm::lookup($id);
 
         return $this->form;
     }
@@ -125,11 +145,33 @@ class Topic {
     }
 
     function isEnabled() {
-         return ($this->ht['isactive']);
+        return $this->isActive();
     }
 
-    function isActive() {
-        return $this->isEnabled();
+    /**
+     * Determine if the help topic is currently enabled. The ancestry of
+     * this topic will be considered to see if any of the parents are
+     * disabled. If any are disabled, then this topic will be considered
+     * disabled.
+     *
+     * Parameters:
+     * $chain - array<id:bool> recusion chain used to detect loops. The
+     *      chain should be maintained and passed to a parent's ::isActive()
+     *      method. When consulting a parent, if the local topic ID is a key
+     *      in the chain, then this topic has already been considered, and
+     *      there is a loop in the ancestry
+     */
+    function isActive(array $chain=array()) {
+        if (!$this->ht['isactive'])
+            return false;
+
+        if (!isset($chain[$this->getId()]) && ($p = $this->getParent())) {
+            $chain[$this->getId()] = true;
+            return $p->isActive($chain);
+        }
+        else {
+            return $this->ht['isactive'];
+        }
     }
 
     function isPublic() {
@@ -144,6 +186,16 @@ class Topic {
         return $this->getHashtable();
     }
 
+    function setSortOrder($i) {
+        if ($i != $this->ht['sort']) {
+            $sql = 'UPDATE '.TOPIC_TABLE.' SET `sort`='.db_input($i)
+                .' WHERE `topic_id`='.db_input($this->getId());
+            return (db_query($sql) && db_affected_rows() == 1);
+        }
+        // Noop
+        return true;
+    }
+
     function update($vars, &$errors) {
 
         if(!$this->save($this->getId(), $vars, $errors))
@@ -154,6 +206,10 @@ class Topic {
     }
 
     function delete() {
+        global $cfg;
+
+        if ($this->getId() == $cfg->getDefaultTopicId())
+            return false;
 
         $sql='DELETE FROM '.TOPIC_TABLE.' WHERE topic_id='.db_input($this->getId()).' LIMIT 1';
         if(db_query($sql) && ($num=db_affected_rows())) {
@@ -169,29 +225,64 @@ class Topic {
         return self::save(0, $vars, $errors);
     }
 
-    function getHelpTopics($publicOnly=false) {
-
-        $topics=array();
-        $sql='SELECT ht.topic_id, CONCAT_WS(" / ", ht2.topic, ht.topic) as name '
-            .' FROM '.TOPIC_TABLE. ' ht '
-            .' LEFT JOIN '.TOPIC_TABLE.' ht2 ON(ht2.topic_id=ht.topic_pid) '
-            .' WHERE ht.isactive=1';
-
-        if($publicOnly)
-            $sql.=' AND ht.ispublic=1';
+    static function getHelpTopics($publicOnly=false, $disabled=false) {
+        global $cfg;
+        static $topics, $names;
+
+        if (!$names) {
+            $sql = 'SELECT topic_id, topic_pid, ispublic, isactive, topic FROM '.TOPIC_TABLE
+                . ' ORDER BY `sort`';
+            $res = db_query($sql);
+
+            // Fetch information for all topics, in declared sort order
+            $topics = array();
+            while (list($id, $pid, $pub, $act, $topic) = db_fetch_row($res))
+                $topics[$id] = array('pid'=>$pid, 'public'=>$pub,
+                    'disabled'=>!$act, 'topic'=>$topic);
+
+            // Resolve parent names
+            foreach ($topics as $id=>$info) {
+                $name = $info['topic'];
+                $loop = array($id=>true);
+                $parent = false;
+                while ($info['pid'] && ($info = $topics[$info['pid']])) {
+                    $name = sprintf('%s / %s', $info['topic'], $name);
+                    if ($parent && $parent['disabled'])
+                        // Cascade disabled flag
+                        $topics[$id]['disabled'] = true;
+                    if (isset($loop[$info['pid']]))
+                        break;
+                    $loop[$info['pid']] = true;
+                    $parent = $info;
+                }
+                $names[$id] = $name;
+            }
+        }
 
-        $sql.=' ORDER BY name';
-        if(($res=db_query($sql)) && db_num_rows($res))
-            while(list($id, $name)=db_fetch_row($res))
-                $topics[$id]=$name;
+        // Apply requested filters
+        $requested_names = array();
+        foreach ($names as $id=>$n) {
+            $info = $topics[$id];
+            if ($publicOnly && !$info['public'])
+                continue;
+            if (!$disabled && $info['disabled'])
+                continue;
+            if ($disabled === self::DISPLAY_DISABLED && $info['disabled'])
+                $n .= " &mdash; (disabled)";
+            $requested_names[$id] = $n;
+        }
 
-        return $topics;
+        return $requested_names;
     }
 
     function getPublicHelpTopics() {
         return self::getHelpTopics(true);
     }
 
+    function getAllHelpTopics() {
+        return self::getHelpTopics(false, true);
+    }
+
     function getIdByName($name, $pid=0) {
 
         $sql='SELECT topic_id FROM '.TOPIC_TABLE
@@ -203,11 +294,12 @@ class Topic {
         return $id;
     }
 
-    function lookup($id) {
+    static function lookup($id) {
         return ($id && is_numeric($id) && ($t= new Topic($id)) && $t->getId()==$id)?$t:null;
     }
 
     function save($id, $vars, &$errors) {
+        global $cfg;
 
         $vars['topic']=Format::striptags(trim($vars['topic']));
 
@@ -218,7 +310,7 @@ class Topic {
             $errors['topic']='Help topic required';
         elseif(strlen($vars['topic'])<5)
             $errors['topic']='Topic is too short. 5 chars minimum';
-        elseif(($tid=self::getIdByName($vars['topic'], $vars['pid'])) && $tid!=$id)
+        elseif(($tid=self::getIdByName($vars['topic'], $vars['topic_pid'])) && $tid!=$id)
             $errors['topic']='Topic already exists';
 
         if (!is_numeric($vars['dept_id']))
@@ -226,13 +318,13 @@ class Topic {
 
         if($errors) return false;
 
-        foreach (array('sla_id','form_id','page_id','pid') as $f)
+        foreach (array('sla_id','form_id','page_id','topic_pid') as $f)
             if (!isset($vars[$f]))
                 $vars[$f] = 0;
 
         $sql=' updated=NOW() '
             .',topic='.db_input($vars['topic'])
-            .',topic_pid='.db_input($vars['pid'])
+            .',topic_pid='.db_input($vars['topic_pid'])
             .',dept_id='.db_input($vars['dept_id'])
             .',priority_id='.db_input($vars['priority_id'])
             .',sla_id='.db_input($vars['sla_id'])
@@ -251,23 +343,46 @@ class Topic {
         else
             $sql.=',staff_id=0, team_id=0 '; //no auto-assignment!
 
-        if($id) {
+        $rv = false;
+        if ($id) {
             $sql='UPDATE '.TOPIC_TABLE.' SET '.$sql.' WHERE topic_id='.db_input($id);
-            if(db_query($sql))
-                return true;
-
-            $errors['err']='Unable to update topic. Internal error occurred';
+            if (!($rv = db_query($sql)))
+                $errors['err']='Unable to update topic. Internal error occurred';
         } else {
             if (isset($vars['topic_id']))
                 $sql .= ', topic_id='.db_input($vars['topic_id']);
-            $sql='INSERT INTO '.TOPIC_TABLE.' SET '.$sql.',created=NOW()';
-            if(db_query($sql) && ($id=db_insert_id()))
-                return $id;
+            if ($vars['topic_pid'] && $cfg->getTopicSortMode() != 'a') {
+                $sql .= ', `sort`='.db_input(
+                    db_result(db_query('SELECT COALESCE(`sort`,0)+1 FROM '.TOPIC_TABLE
+                        .' WHERE `topic_id`='.db_input($vars['topic_pid']))));
+            }
 
-            $errors['err']='Unable to create the topic. Internal error';
+            $sql='INSERT INTO '.TOPIC_TABLE.' SET '.$sql.',created=NOW()';
+            if (db_query($sql) && ($id = db_insert_id()))
+                $rv = $id;
+            else
+                $errors['err']='Unable to create the topic. Internal error';
         }
+        if ($cfg->getTopicSortMode() == 'a') {
+            static::updateSortOrder();
+        }
+        return $rv;
+    }
 
-        return false;
+    static function updateSortOrder() {
+        // Fetch (un)sorted names
+        $names = static::getHelpTopics(false, true);
+        uasort($names, function($a, $b) { return strcmp($a, $b); });
+
+        $update = array_keys($names);
+        foreach ($update as $idx=>&$id) {
+            $id = sprintf("(%s,%s)", db_input($id), db_input($idx+1));
+        }
+        // Thanks, http://stackoverflow.com/a/3466
+        $sql = sprintf('INSERT INTO `%s` (topic_id,`sort`) VALUES %s
+            ON DUPLICATE KEY UPDATE `sort`=VALUES(`sort`)',
+            TOPIC_TABLE, implode(',', $update));
+        db_query($sql);
     }
 }
 
diff --git a/include/client/open.inc.php b/include/client/open.inc.php
index 26b6d30edf9ab857dd1fb5b25f63bcfbc5a22c3e..9cb1bb65739a4c3504ef701ae128378c943f91b0 100644
--- a/include/client/open.inc.php
+++ b/include/client/open.inc.php
@@ -10,6 +10,9 @@ if($thisclient && $thisclient->isValid()) {
 $info=($_POST && $errors)?Format::htmlchars($_POST):$info;
 
 $form = null;
+if (!$info['topicId'])
+    $info['topicId'] = $cfg->getDefaultTopicId();
+
 if ($info['topicId'] && ($topic=Topic::lookup($info['topicId']))) {
     $form = $topic->getForm();
     if ($_POST && $form) {
@@ -30,8 +33,9 @@ if ($info['topicId'] && ($topic=Topic::lookup($info['topicId']))) {
         <td class="required">Help Topic:</td>
         <td>
             <select id="topicId" name="topicId" onchange="javascript:
+                    var data = $(':input[name]', '#dynamic-form').serialize();
                     $('#dynamic-form').load(
-                        'ajax.php/form/help-topic/' + this.value);
+                        'ajax.php/form/help-topic/' + this.value, data);
                     ">
                 <option value="" selected="selected">&mdash; Select a Help Topic &mdash;</option>
                 <?php
diff --git a/include/staff/email.inc.php b/include/staff/email.inc.php
index e8d3a430e74c3f66a40dc374c1974b2dd3a9fa6d..8f8e683121a80ca852c4b65073c899bbbbe658a2 100644
--- a/include/staff/email.inc.php
+++ b/include/staff/email.inc.php
@@ -127,16 +127,13 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
             <td>
 		<span>
 			<select name="topic_id">
-			    <option value="0" selected="selected">&mdash; None &mdash;</option>
+			    <option value="0" selected="selected">&mdash; System Default &mdash;</option>
 			    <?php
-			    $sql='SELECT topic_id, topic FROM '.TOPIC_TABLE.' T ORDER by topic';
-			    if(($res=db_query($sql)) && db_num_rows($res)){
-				while(list($id,$name)=db_fetch_row($res)){
-				    $selected=($info['topic_id'] && $id==$info['topic_id'])?'selected="selected"':'';
-				    echo sprintf('<option value="%d" %s>%s</option>',$id,$selected,$name);
-				}
-			    }
-			    ?>
+                    $topics = Topic::getHelpTopics();
+                    while (list($id,$topic) = each($topics)) { ?>
+                        <option value="<?php echo $id; ?>"<?php echo ($info['topic_id']==$id)?'selected':''; ?>><?php echo $topic; ?></option>
+                    <?php
+                    } ?>
 			</select>
 			<i class="help-tip icon-question-sign" href="#new_ticket_help_topic"></i>
 		</span>
diff --git a/include/staff/helptopic.inc.php b/include/staff/helptopic.inc.php
index 6e4317cb15d890de7732766139f9673f87cd0c9e..00f06d38d0ca604b2adf81daa606132110fd7359 100644
--- a/include/staff/helptopic.inc.php
+++ b/include/staff/helptopic.inc.php
@@ -16,6 +16,7 @@ if($topic && $_REQUEST['a']!='add') {
     $submit_text='Add Topic';
     $info['isactive']=isset($info['isactive'])?$info['isactive']:1;
     $info['ispublic']=isset($info['ispublic'])?$info['ispublic']:1;
+    $info['form_id'] = Topic::FORM_USE_PARENT;
     $qstr.='&a='.$_REQUEST['a'];
 }
 $info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
@@ -70,19 +71,15 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
                 Parent Topic:
             </td>
             <td>
-                <select name="pid">
-                    <option value="">&mdash; Select Parent Topic &mdash;</option>
+                <select name="topic_pid">
+                    <option value="">&mdash; Top-Level Topic &mdash;</option><?php
+                    $topics = Topic::getAllHelpTopics();
+                    while (list($id,$topic) = each($topics)) {
+                        if ($id == $info['topic_id'])
+                            continue; ?>
+                        <option value="<?php echo $id; ?>"<?php echo ($info['topic_pid']==$id)?'selected':''; ?>><?php echo $topic; ?></option>
                     <?php
-                    $sql='SELECT topic_id, topic FROM '.TOPIC_TABLE
-                        .' WHERE topic_pid=0 '
-                        .' ORDER by topic';
-                    if(($res=db_query($sql)) && db_num_rows($res)) {
-                        while(list($id, $name)=db_fetch_row($res)) {
-                            echo sprintf('<option value="%d" %s>%s</option>',
-                                    $id, (($info['pid'] && $id==$info['pid'])?'selected="selected"':'') ,$name);
-                        }
-                    }
-                    ?>
+                    } ?>
                 </select> <i class="help-tip icon-question-sign" href="#parent_topic"></i>
                 &nbsp;<span class="error">&nbsp;<?php echo $errors['pid']; ?></span>
             </td>
@@ -92,9 +89,14 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
         <tr>
            <td><strong>Custom Form</strong>:</td>
            <td><select name="form_id">
-               <option value="0">&mdash; No Extra Fields &mdash;</option>
+                <option value="0" <?php
+if ($info['form_id'] == '0') echo 'selected="selected"';
+                    ?>>&mdash; None &mdash;</option>
+                <option value="<?php echo Topic::FORM_USE_PARENT; ?>"  <?php
+if ($info['form_id'] == Topic::FORM_USE_PARENT) echo 'selected="selected"';
+                    ?>>&mdash; Use Parent Form &mdash;</option>
                <?php foreach (DynamicForm::objects()->filter(array('type'=>'G')) as $group) { ?>
-                   <option value="<?php echo $group->get('id'); ?>"
+                <option value="<?php echo $group->get('id'); ?>"
                        <?php if ($group->get('id') == $info['form_id'])
                             echo 'selected="selected"'; ?>>
                        <?php echo $group->get('title'); ?>
diff --git a/include/staff/helptopics.inc.php b/include/staff/helptopics.inc.php
index 8395a29e3d723ed881405cfb8d06f96bbad728bc..04d3b5238189b00f1d49b7c1e2706cf7c703ac42 100644
--- a/include/staff/helptopics.inc.php
+++ b/include/staff/helptopics.inc.php
@@ -1,51 +1,35 @@
 <?php
 if(!defined('OSTADMININC') || !$thisstaff->isAdmin()) die('Access Denied');
 
-$qstr='';
 $sql='SELECT topic.* '
-    .', IF(ptopic.topic_pid IS NULL, topic.topic, CONCAT_WS(" / ", ptopic.topic, topic.topic)) as name '
     .', dept.dept_name as department '
     .', priority_desc as priority '
     .' FROM '.TOPIC_TABLE.' topic '
-    .' LEFT JOIN '.TOPIC_TABLE.' ptopic ON (ptopic.topic_id=topic.topic_pid) '
     .' LEFT JOIN '.DEPT_TABLE.' dept ON (dept.dept_id=topic.dept_id) '
     .' LEFT JOIN '.TICKET_PRIORITY_TABLE.' pri ON (pri.priority_id=topic.priority_id) ';
 $sql.=' WHERE 1';
-$sortOptions=array('name'=>'name','status'=>'topic.isactive','type'=>'topic.ispublic',
-                   'dept'=>'department','priority'=>'priority','updated'=>'topic.updated');
-$orderWays=array('DESC'=>'DESC','ASC'=>'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:'topic.topic';
+$order_by = ($cfg->getTopicSortMode() == 'm' ? '`sort`' : '`topic_id`');
 
-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 '.TOPIC_TABLE.' topic ');
 $page=($_GET['p'] && is_numeric($_GET['p']))?$_GET['p']:1;
-$pageNav=new Pagenate($total, $page, PAGE_LIMIT);
-$pageNav->setURL('helptopics.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 GROUP BY topic.topic_id ORDER BY $order_by LIMIT ".$pageNav->getStart().",".$pageNav->getLimit();
+$query="$sql ORDER BY $order_by";
 $res=db_query($query);
 if($res && ($num=db_num_rows($res)))
-    $showing=$pageNav->showing().' help topics';
+    $showing="Showing $num help topics";
 else
     $showing='No help topic found!';
 
+// Get the full names and filter for this page
+$topics = array();
+while ($row = db_fetch_array($res))
+    $topics[] = $row;
+
+foreach ($topics as &$t)
+    $t['name'] = Topic::getTopicName($t['topic_id']);
+
+if ($cfg->getTopicSortMode() == 'a')
+    usort($topics, function($a, $b) { return strcmp($a['name'], $b['name']); });
+
 ?>
 <div style="width:700px;padding-top:5px; float:left;">
  <h2>Help Topics</h2>
@@ -56,28 +40,44 @@ else
 <form action="helptopics.php" method="POST" name="topics">
  <?php csrf_token(); ?>
  <input type="hidden" name="do" value="mass_process" >
-<input type="hidden" id="action" name="a" value="" >
+<input type="hidden" id="action" name="a" value="sort" >
  <table class="list" border="0" cellspacing="1" cellpadding="0" width="940">
-    <caption><?php echo $showing; ?></caption>
+    <caption><span style="display:inline-block;vertical-align:middle"><?php
+         echo $showing; ?></span>
+    <div class="pull-right">Sorting Mode:
+        <select name="help_topic_sort_mode" onchange="javascript:
+    var $form = $(this).closest('form');
+    $form.find('input[name=a]').val('sort');
+    $form.submit();
+">
+<?php foreach (OsticketConfig::allTopicSortModes() as $i=>$m)
+    echo sprintf('<option value="%s"%s>%s</option>',
+        $i, $i == $cfg->getTopicSortMode() ? ' selected="selected"' : '', $m); ?>
+        </select>
+    </div>
+    </caption>
     <thead>
         <tr>
-            <th width="7">&nbsp;</th>
-            <th width="320"><a <?php echo $name_sort; ?> href="helptopics.php?<?php echo $qstr; ?>&sort=name">Help Topic</a></th>
-            <th width="80"><a  <?php echo $status_sort; ?> href="helptopics.php?<?php echo $qstr; ?>&sort=status">Status</a></th>
-            <th width="100"><a  <?php echo $type_sort; ?> href="helptopics.php?<?php echo $qstr; ?>&sort=type">Type</a></th>
-            <th width="100"><a  <?php echo $priority_sort; ?> href="helptopics.php?<?php echo $qstr; ?>&sort=priority">Priority</a></th>
-            <th width="200"><a  <?php echo $dept_sort; ?> href="helptopics.php?<?php echo $qstr; ?>&sort=dept">Department</a></th>
-            <th width="150" nowrap><a  <?php echo $updated_sort; ?>href="helptopics.php?<?php echo $qstr; ?>&sort=updated">Last Updated</a></th>
+            <th width="7" style="height:20px;">&nbsp;</th>
+            <th style="padding-left:4px;vertical-align:middle" width="360">Help Topic</th>
+            <th style="padding-left:4px;vertical-align:middle" width="80">Status</th>
+            <th style="padding-left:4px;vertical-align:middle" width="100">Type</th>
+            <th style="padding-left:4px;vertical-align:middle" width="100">Priority</th>
+            <th style="padding-left:4px;vertical-align:middle" width="160">Department</th>
+            <th style="padding-left:4px;vertical-align:middle" width="150" nowrap>Last Updated</th>
         </tr>
     </thead>
-    <tbody>
+    <tbody class="<?php if ($cfg->getTopicSortMode() == 'm') echo 'sortable-rows'; ?>"
+        data-sort="sort-">
     <?php
         $total=0;
         $ids=($errors && is_array($_POST['ids']))?$_POST['ids']:null;
-        if($res && db_num_rows($res)):
+        if (count($topics)):
             $defaultDept = $cfg->getDefaultDept();
             $defaultPriority = $cfg->getDefaultPriority();
-            while ($row = db_fetch_array($res)) {
+            $sort = 0;
+            foreach($topics as $row) {
+                $sort++; // Track initial order for transition
                 $sel=false;
                 if($ids && in_array($row['topic_id'],$ids))
                     $sel=true;
@@ -93,10 +93,16 @@ else
                 ?>
             <tr id="<?php echo $row['topic_id']; ?>">
                 <td width=7px>
+                  <input type="hidden" name="sort-<?php echo $row['topic_id']; ?>" value="<?php
+                        echo $row['sort'] ?: $sort; ?>"/>
                   <input type="checkbox" class="ckb" name="ids[]" value="<?php echo $row['topic_id']; ?>"
                             <?php echo $sel?'checked="checked"':''; ?>>
                 </td>
-                <td><a href="helptopics.php?id=<?php echo $row['topic_id']; ?>"><?php echo $row['name']; ?></a>&nbsp;</td>
+                <td>
+<?php if ($cfg->getTopicSortMode() == 'm') { ?>
+                    <i class="icon-sort"></i>
+<?php } ?>
+<a href="helptopics.php?id=<?php echo $row['topic_id']; ?>"><?php echo $row['name']; ?></a>&nbsp;</td>
                 <td><?php echo $row['isactive']?'Active':'<b>Disabled</b>'; ?></td>
                 <td><?php echo $row['ispublic']?'Public':'<b>Private</b>'; ?></td>
                 <td><?php echo $row['priority']; ?></td>
@@ -123,12 +129,14 @@ else
 </table>
 <?php
 if($res && $num): //Show options..
-    echo '<div>&nbsp;Page:'.$pageNav->getPageLinks().'&nbsp;</div>';
 ?>
 <p class="centered" id="actions">
-    <input class="button" type="submit" name="enable" value="Enable" >
-    <input class="button" type="submit" name="disable" value="Disable">
-    <input class="button" type="submit" name="delete" value="Delete">
+<?php if ($cfg->getTopicSortMode() != 'a') { ?>
+    <input class="button no-confirm" type="submit" name="sort" value="Save"/>
+<?php } ?>
+    <button class="button" type="submit" name="enable" value="Enable" >Enable</button>
+    <button class="button" type="submit" name="disable" value="Disable">Disable</button>
+    <button class="button" type="submit" name="delete" value="Delete">Delete</button>
 </p>
 <?php
 endif;
diff --git a/include/staff/settings-tickets.inc.php b/include/staff/settings-tickets.inc.php
index 37bc2af8561a8e12252dca25e9ef10f1167e3c7e..a7960a02b16552d72bd5377a138ccc049b535d46 100644
--- a/include/staff/settings-tickets.inc.php
+++ b/include/staff/settings-tickets.inc.php
@@ -63,6 +63,20 @@ if(!($maxfileuploads=ini_get('max_file_uploads')))
                 &nbsp;<span class="error">*&nbsp;<?php echo $errors['default_priority_id']; ?></span> <i class="help-tip icon-question-sign" href="#default_priority"></i>
              </td>
         </tr>
+        <tr>
+            <td width="180">Default Help Topic:</td>
+            <td>
+                <select name="default_help_topic">
+                    <option value="0">&mdash; None &mdash;</option><?php
+                    $topics = Topic::getHelpTopics(false, Topic::DISPLAY_DISABLED);
+                    while (list($id,$topic) = each($topics)) { ?>
+                        <option value="<?php echo $id; ?>"<?php echo ($config['default_help_topic']==$id)?'selected':''; ?>><?php echo $topic; ?></option>
+                    <?php
+                    } ?>
+                </select><br/>
+                <span class="error"><?php echo $errors['default_help_topic']; ?></span>
+            </td>
+        </tr>
         <tr>
             <td>Maximum <b>Open</b> Tickets:</td>
             <td>
@@ -88,7 +102,7 @@ if(!($maxfileuploads=ini_get('max_file_uploads')))
             <td>Claim on Response:</td>
             <td>
                 <input type="checkbox" name="auto_claim_tickets" <?php echo $config['auto_claim_tickets']?'checked="checked"':''; ?>>
-                &nbsp;Enable&nbsp;<i class="help-tip icon-question-sign" href="#claim_tickets"></i>
+                Enable&nbsp;<i class="help-tip icon-question-sign" href="#claim_tickets"></i>
             </td>
         </tr>
         <tr>
diff --git a/include/staff/ticket-open.inc.php b/include/staff/ticket-open.inc.php
index d14f7e98e393e6ed10d5a03c9ce5523e8fdbe6a8..bfd0571bdbc0ffb660831a54f2c463ba095e43b7 100644
--- a/include/staff/ticket-open.inc.php
+++ b/include/staff/ticket-open.inc.php
@@ -3,6 +3,9 @@ if(!defined('OSTSCPINC') || !$thisstaff || !$thisstaff->canCreateTickets()) die(
 $info=array();
 $info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
 
+if (!$info['topicId'])
+    $info['topicId'] = $cfg->getDefaultTopicId();
+
 $form = null;
 if ($info['topicId'] && ($topic=Topic::lookup($info['topicId']))) {
     $form = $topic->getForm();
@@ -124,8 +127,9 @@ if ($info['topicId'] && ($topic=Topic::lookup($info['topicId']))) {
             </td>
             <td>
                 <select name="topicId" onchange="javascript:
+                        var data = $(':input[name]', '#dynamic-form').serialize();
                         $('#dynamic-form').load(
-                            'ajax.php/form/help-topic/' + this.value);
+                            'ajax.php/form/help-topic/' + this.value, data);
                         ">
                     <?php
                     if ($topics=Topic::getHelpTopics()) {
diff --git a/open.php b/open.php
index e0bc8fdcfb1ba75a1efca191e0a31cc06ac36930..fe454521b8b6561f616a125d0ee3b699239ebcc0 100644
--- a/open.php
+++ b/open.php
@@ -38,6 +38,8 @@ if ($_POST) {
     //Ticket::create...checks for errors..
     if(($ticket=Ticket::create($vars, $errors, SOURCE))){
         $msg='Support ticket request created';
+        // Drop session-backed form data
+        unset($_SESSION[':form-data']);
         //Logged in...simply view the newly created ticket.
         if($thisclient && $thisclient->isValid()) {
             session_write_close();
diff --git a/scp/css/scp.css b/scp/css/scp.css
index e1721f6e353256052bd87c85a48749c6f61f676e..e99d9c36bce30f4c7e0b57f83e410dc0efc2ddf8 100644
--- a/scp/css/scp.css
+++ b/scp/css/scp.css
@@ -454,9 +454,9 @@ table.list tbody td {
 }
 
 table.list tbody td { background: #fff; padding: 1px; padding-left:2px; vertical-align: top; }
-table.list tbody tr.odd td { background-color: #f0faff; }
+table.list tbody tr:nth-child(2n+1) td { background-color: #f0faff; }
 table.list tbody tr:hover td { background: #ffe; }
-table.list tbody tr.odd:hover td { background: #ffd; }
+table.list tbody tr:nth-child(2n+1):hover td { background: #ffd; }
 /* row highlighting on hover + select */
 table.list tbody tr:hover td, table.list tbody tr.highlight td {  background: #FFFFDD; }
 /* disabled highlighting on nohover */
@@ -530,7 +530,7 @@ a.print {
     color:#000;
 }
 
-.button { padding:1px 5px; margin-right:10px;  color:#777; font-weight:bold;}
+#actions button, .button { padding:2px 5px 3px; margin-right:10px;  color:#777;}
 
 .btn_sm {
     padding:2px 5px;
@@ -1642,6 +1642,10 @@ div.selected-signature .inner {
     right: 5px;
 }
 
+.sortable-rows tr td:hover {
+    cursor: move;
+}
+
 .sortable-row-item {
     border: 1px solid rgba(0, 0, 0, 0.7);
     padding: 9px;
diff --git a/scp/helptopics.php b/scp/helptopics.php
index 0847257cfc0d3da5c7d95a79773f0d34062c0b33..b33cad9a5e79b77fae4adce850a6af88c36dc943 100644
--- a/scp/helptopics.php
+++ b/scp/helptopics.php
@@ -41,9 +41,15 @@ if($_POST){
             }
             break;
         case 'mass_process':
-            if(!$_POST['ids'] || !is_array($_POST['ids']) || !count($_POST['ids'])) {
-                $errors['err'] = 'You must select at least one help topic';
-            } else {
+            switch(strtolower($_POST['a'])) {
+            case 'sort':
+                // Pass
+                break;
+            default:
+                if(!$_POST['ids'] || !is_array($_POST['ids']) || !count($_POST['ids']))
+                    $errors['err'] = 'You must select at least one help topic';
+            }
+            if (!$errors) {
                 $count=count($_POST['ids']);
 
                 switch(strtolower($_POST['a'])) {
@@ -62,7 +68,8 @@ if($_POST){
                         break;
                     case 'disable':
                         $sql='UPDATE '.TOPIC_TABLE.' SET isactive=0 '
-                            .' WHERE topic_id IN ('.implode(',', db_input($_POST['ids'])).')';
+                            .' WHERE topic_id IN ('.implode(',', db_input($_POST['ids'])).')'
+                            .' AND topic_id <> '.db_input($cfg->getDefaultTopicId());
                         if(db_query($sql) && ($num=db_affected_rows())) {
                             if($num==$count)
                                 $msg = 'Selected help topics disabled';
@@ -86,6 +93,23 @@ if($_POST){
                         elseif(!$errors['err'])
                             $errors['err']  = 'Unable to delete selected help topics';
 
+                        break;
+                    case 'sort':
+                        try {
+                            $cfg->setTopicSortMode($_POST['help_topic_sort_mode']);
+                            if ($cfg->getTopicSortMode() == 'm') {
+                                foreach ($_POST as $k=>$v) {
+                                    if (strpos($k, 'sort-') === 0
+                                            && is_numeric($v)
+                                            && ($t = Topic::lookup(substr($k, 5))))
+                                        $t->setSortOrder($v);
+                                }
+                            }
+                            $msg = 'Successfully set sorting configuration';
+                        }
+                        catch (Exception $ex) {
+                            $errors['err'] = 'Unable to set sorting mode';
+                        }
                         break;
                     default:
                         $errors['err']='Unknown action - get technical help.';
diff --git a/scp/js/scp.js b/scp/js/scp.js
index b2e0ba77b9a9a1d95555194884d08441b86f3d1a..24efdcab291c6a2711ebe2cdce89c7d3e6bd0350 100644
--- a/scp/js/scp.js
+++ b/scp/js/scp.js
@@ -32,7 +32,6 @@ function checkbox_checker(formObj, min, max) {
 var scp_prep = function() {
 
     $("input:not(.dp):visible:enabled:first").focus();
-    $('table.list tbody tr:odd').addClass('odd');
     $('table.list input:checkbox').bind('click, change', function() {
         $(this)
             .parents("tr:first")
@@ -76,7 +75,7 @@ var scp_prep = function() {
         return false;
      });
 
-    $('#actions input:submit.button').bind('click', function(e) {
+    $('#actions :submit.button:not(.no-confirm)').bind('click', function(e) {
 
         var formObj = $(this).closest('form');
         e.preventDefault();
@@ -437,6 +436,7 @@ var scp_prep = function() {
    // Sortable tables for dynamic forms objects
    $('.sortable-rows').sortable({
        'helper': fixHelper,
+       'cursor': 'move',
        'stop': function(e, ui) {
            var attr = ui.item.parent('tbody').data('sort');
            warnOnLeave(ui.item);
diff --git a/scp/tickets.php b/scp/tickets.php
index 82f0e569fd6926ba949f1d41aa6458e3c647e170..16b9d4cde3518968d6fe70a1b345c83c539d5d37 100644
--- a/scp/tickets.php
+++ b/scp/tickets.php
@@ -497,6 +497,7 @@ if($_POST && !$errors):
                         if (!$ticket->checkStaffAccess($thisstaff) || $ticket->isClosed())
                             $ticket=null;
                         Draft::deleteForNamespace('ticket.staff%', $thisstaff->getId());
+                        unset($_SESSION[':form-data']);
                     } elseif(!$errors['err']) {
                         $errors['err']='Unable to create the ticket. Correct the error(s) and try again';
                     }