diff --git a/bootstrap.php b/bootstrap.php
index d2afa2243f7bfa2914da15428c3f8e2c9b197fcb..63d71fe8754d51c5d110ec1d9fa447dc0bfefdac 100644
--- a/bootstrap.php
+++ b/bootstrap.php
@@ -116,6 +116,7 @@ class Bootstrap {
         define('FORM_ANSWER_TABLE',$prefix.'form_entry_values');
 
         define('TOPIC_TABLE',$prefix.'help_topic');
+        define('TOPIC_FORM_TABLE',$prefix.'help_topic_form');
         define('SLA_TABLE', $prefix.'sla');
 
         define('EMAIL_TABLE',$prefix.'email');
diff --git a/include/ajax.forms.php b/include/ajax.forms.php
index 6c35f1bb2fe2055d220c89c583261a189223ddc0..169290c945bac96986ef2ef222710691256851b5 100644
--- a/include/ajax.forms.php
+++ b/include/ajax.forms.php
@@ -24,13 +24,13 @@ class DynamicFormsAjaxAPI extends AjaxController {
             $_SESSION[':form-data'] = array_merge($_SESSION[':form-data'], $_GET);
         }
 
-        if ($form = $topic->getForm()) {
+        foreach ($topic->getForms() as $form) {
             ob_start();
             $form->getForm($_SESSION[':form-data'])->render(!$client);
-            $html = ob_get_clean();
+            $html .= ob_get_clean();
             ob_start();
             print $form->getMedia();
-            $media = ob_get_clean();
+            $media .= ob_get_clean();
         }
         return $this->encode(array(
             'media' => $media,
@@ -140,5 +140,23 @@ class DynamicFormsAjaxAPI extends AjaxController {
             array('id'=>$field->ajaxUpload(true))
         );
     }
+
+    function getAllFields($id) {
+        global $thisstaff;
+
+        if (!$thisstaff)
+            Http::response(403, 'Login required');
+        elseif (!$form = DynamicForm::lookup($id))
+            Http::response(400, 'No such form');
+
+        ob_start();
+        include STAFFINC_DIR . 'templates/dynamic-form-fields-view.tmpl.php';
+        $html = ob_get_clean();
+
+        return $this->encode(array(
+            'success'=>true,
+            'html' => $html,
+        ));
+    }
 }
 ?>
diff --git a/include/class.dynamic_forms.php b/include/class.dynamic_forms.php
index 181c1bdbd524bc8c3c2582c874b03ca127738ba9..cdc6217228ed501bc51f01f9520db45110753ded 100644
--- a/include/class.dynamic_forms.php
+++ b/include/class.dynamic_forms.php
@@ -32,6 +32,11 @@ class DynamicForm extends VerySimpleModel {
         'table' => FORM_SEC_TABLE,
         'ordering' => array('title'),
         'pk' => array('id'),
+        'joins' => array(
+            'fields' => array(
+                'reverse' => 'DynamicFormField.form',
+            ),
+        ),
     );
 
     // Registered form types
@@ -112,6 +117,14 @@ class DynamicForm extends VerySimpleModel {
         return $this->get('deletable');
     }
 
+    function disableFields(array $ids) {
+        foreach ($this->getFields() as $F) {
+            if (in_array($F->get('id'), $ids)) {
+                $F->disable();
+            }
+        }
+    }
+
     function instanciate($sort=1) {
         return DynamicFormEntry::create(array(
             'form_id'=>$this->get('id'), 'sort'=>$sort));
@@ -422,6 +435,7 @@ class DynamicFormField extends VerySimpleModel {
     );
 
     var $_field;
+    var $_disabled = false;
 
     const FLAG_ENABLED          = 0x00001;
     const FLAG_EXT_STORED       = 0x00002; // Value stored outside of form_entry_value
@@ -525,6 +539,12 @@ class DynamicFormField extends VerySimpleModel {
     function  isEditable() {
         return $this->hasFlag(self::FLAG_MASK_EDIT);
     }
+    function disable() {
+        $this->_disabled = true;
+    }
+    function isEnabled() {
+        return !$this->_disabled && $this->hasFlag(self::FLAG_ENABLED);
+    }
 
     function hasFlag($flag) {
         return (isset($this->flags) && ($this->flags & $flag) != 0);
@@ -638,19 +658,19 @@ class DynamicFormField extends VerySimpleModel {
         return $this->hasFlag(self::FLAG_CLOSE_REQUIRED);
     }
     function isEditableToStaff() {
-        return $this->hasFlag(self::FLAG_ENABLED)
+        return $this->isEnabled()
             && $this->hasFlag(self::FLAG_AGENT_EDIT);
     }
     function isVisibleToStaff() {
-        return $this->hasFlag(self::FLAG_ENABLED)
+        return $this->isEnabled()
             && $this->hasFlag(self::FLAG_AGENT_VIEW);
     }
     function isEditableToUsers() {
-        return $this->hasFlag(self::FLAG_ENABLED)
+        return $this->isEnabled()
             && $this->hasFlag(self::FLAG_CLIENT_EDIT);
     }
     function isVisibleToUsers() {
-        return $this->hasFlag(self::FLAG_ENABLED)
+        return $this->isEnabled()
             && $this->hasFlag(self::FLAG_CLIENT_VIEW);
     }
 
@@ -722,7 +742,7 @@ class DynamicFormEntry extends VerySimpleModel {
         'pk' => array('id'),
         'select_related' => array('form'),
         'fields' => array('id', 'form_id', 'object_type', 'object_id',
-            'sort', 'updated', 'created'),
+            'sort', 'extra', 'updated', 'created'),
         'joins' => array(
             'form' => array(
                 'null' => true,
@@ -785,6 +805,10 @@ class DynamicFormEntry extends VerySimpleModel {
             $this->_form = DynamicForm::lookup($this->get('form_id'));
             if ($this->_form && isset($this->id))
                 $this->_form->data($this);
+            if (isset($this->extra)) {
+                $x = JsonDataParser::decode($this->extra) ?: array();
+                $this->_form->disableFields($x['disable'] ?: array());
+            }
         }
         return $this->_form;
     }
@@ -958,6 +982,7 @@ class DynamicFormEntry extends VerySimpleModel {
                 }
             }
             if (!$found && ($fImpl = $field->getImpl($field))
+                    && $field->isEnabled()
                     && !$fImpl->isPresentationOnly()) {
                 $a = DynamicFormEntryAnswer::create(
                     array('field_id'=>$field->get('id'), 'entry_id'=>$this->id));
diff --git a/include/class.ticket.php b/include/class.ticket.php
index 1dd62e97cf49c0be0a0a727260f54062f8d0c8b3..dc6f5fe311c7b65be9b4231649a7cbd553447554 100644
--- a/include/class.ticket.php
+++ b/include/class.ticket.php
@@ -2758,20 +2758,43 @@ class Ticket {
 
         if (!$errors) {
 
-            # Perform ticket filter actions on the new ticket arguments
-            $__form = null;
+            // Handle the forms associate with the help topics. Instanciate the
+            // entries, disable and track the requested disabled fields.
+            $topic_forms = array();
             if ($vars['topicId']) {
-                if (($__topic=Topic::lookup($vars['topicId']))
-                    && ($__form = $__topic->getForm())
-                ) {
-                    $__form = $__form->instanciate();
-                    $__form->setSource($vars);
+                if ($__topic=Topic::lookup($vars['topicId'])) {
+                    foreach ($__topic->getForms() as $idx=>$__F) {
+                        $disabled = array();
+                        foreach ($__F->getFields() as $field) {
+                            if (!$field->isEnabled() && $field->hasFlag(DynamicFormField::FLAG_ENABLED))
+                                $disabled[] = $field->get('id');
+                        }
+                        // Special handling for the ticket form — disable fields
+                        // requested to be disabled as per the help topic.
+                        if ($__F->get('type') == 'T') {
+                            foreach ($form->getFields() as $field) {
+                                if (false !== array_search($field->get('id'), $disabled))
+                                    $field->disable();
+                            }
+                            $form->sort = $idx;
+                            $__F = $form;
+                        }
+                        else {
+                            $__F = $__F->instanciate($idx);
+                            $__F->setSource($vars);
+                            $topic_forms[] = $__F;
+                        }
+                        // Track fields currently disabled
+                        $__F->extra = JsonDataEncoder::encode(array(
+                            'disable' => $disabled
+                        ));
+                    }
                 }
             }
 
             try {
                 $vars = self::filterTicketData($origin, $vars,
-                    array($form, $__form), $user);
+                    array_merge(array($form), $topic_forms), $user);
             }
             catch (RejectedException $ex) {
                 return $reject_ticket(
@@ -2825,10 +2848,8 @@ class Ticket {
 
         if ($vars['topicId']) {
             if ($topic=Topic::lookup($vars['topicId'])) {
-                if ($topic_form = $topic->getForm()) {
-                    $TF = $topic_form->getForm($vars);
-                    $topic_form = $topic_form->instanciate();
-                    $topic_form->setSource($vars);
+                foreach ($topic_forms as $topic_form) {
+                    $TF = $topic_form->getForm()->getForm($vars);
                     if (!$TF->isValid($field_filter('topic')))
                         $errors = array_merge($errors, $TF->errors());
                 }
@@ -2972,7 +2993,7 @@ class Ticket {
         $form->save();
 
         // Save the form data from the help-topic form, if any
-        if ($topic_form) {
+        foreach ($topic_forms as $topic_form) {
             $topic_form->setTicketId($id);
             $topic_form->save();
         }
diff --git a/include/class.topic.php b/include/class.topic.php
index 5adc575635e0fc40f214778ad4da2d2622948177..9f02858efe744889eef7d9fae6707f142791a105 100644
--- a/include/class.topic.php
+++ b/include/class.topic.php
@@ -52,11 +52,15 @@ class Topic extends VerySimpleModel {
                     'priority_id' => 'Priority.priority_id',
                 ),
             ),
+            'forms' => array(
+                'reverse' => 'TopicFormModel.topic',
+                'null' => true,
+            ),
         ),
     );
 
     var $page;
-    var $form;
+    var $_forms;
 
     const DISPLAY_DISABLED = 2;
 
@@ -129,19 +133,15 @@ class Topic extends VerySimpleModel {
         return $this->page;
     }
 
-    function getFormId() {
-        return $this->form_id;
-    }
-
-    function getForm() {
-        $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;
+    function getForms() {
+        if (!isset($this->_forms)) {
+            foreach ($this->forms->select_related('form') as $F) {
+                $extra = JsonDataParser::decode($F->extra) ?: array();
+                $F->form->disableFields($extra['disable'] ?: array());
+                $this->_forms[] = $F->form;
+            }
+        }
+        return $this->_forms;
     }
 
     function autoRespond() {
@@ -426,12 +426,63 @@ class Topic extends VerySimpleModel {
             $errors['err']=sprintf(__('Unable to update %s.'), __('this help topic'))
             .' '.__('Internal error occurred');
         }
-        if (!$cfg || $cfg->getTopicSortMode() == 'a') {
-            static::updateSortOrder();
+        if ($rv) {
+            if (!$cfg || $cfg->getTopicSortMode() == 'a') {
+                static::updateSortOrder();
+            }
+            $this->updateForms($vars, $errors);
         }
         return $rv;
     }
 
+    function updateForms($vars, &$errors) {
+        $find_disabled = function($form) use ($vars) {
+            $fields = $vars['fields'];
+            $disabled = array();
+            foreach ($form->fields->values_flat('id') as $row) {
+                list($id) = $row;
+                if (false === ($idx = array_search($id, $fields))) {
+                    $disabled[] = $id;
+                }
+            }
+            return $disabled;
+        };
+
+        // Consider all the forms in the request
+        if (is_array($form_ids = $vars['forms'])) {
+            $forms = TopicFormModel::objects()
+                ->select_related('form')
+                ->filter(array('topic_id' => $this->getId()));
+            foreach ($forms as $F) {
+                if (false !== ($idx = array_search($F->form_id, $form_ids))) {
+                    $F->sort = $idx + 1;
+                    $F->extra = JsonDataEncoder::encode(
+                        array('disable' => $find_disabled($F->form))
+                    );
+                    $F->save();
+                    unset($form_ids[$idx]);
+                }
+                elseif ($F->form->get('type') != 'T') {
+                    $F->delete();
+                }
+            }
+            foreach ($form_ids as $sort=>$id) {
+                if (!($form = DynamicForm::lookup($id))) {
+                    continue;
+                }
+                TopicFormModel::create(array(
+                    'topic_id' => $this->getId(),
+                    'form_id' => $id,
+                    'sort' => $sort + 1,
+                    'extra' => JsonDataEncoder::encode(
+                        array('disable' => $find_disabled($form))
+                    )
+                ))->save();
+            }
+        }
+        return true;
+    }
+
     function save($refetch=false) {
         if ($this->dirty)
             $this->updated = SqlFunction::NOW();
@@ -473,3 +524,19 @@ class Topic extends VerySimpleModel {
 
 // Add fields from the standard ticket form to the ticket filterable fields
 Filter::addSupportedMatches(/* @trans */ 'Help Topic', array('topicId' => 'Topic ID'), 100);
+
+class TopicFormModel extends VerySimpleModel {
+    static $meta = array(
+        'table' => TOPIC_FORM_TABLE,
+        'pk' => array('id'),
+        'ordering' => array('sort'),
+        'joins' => array(
+            'topic' => array(
+                'constraint' => array('topic_id' => 'Topic.topic_id'),
+            ),
+            'form' => array(
+                'constraint' => array('form_id' => 'DynamicForm.id'),
+            ),
+        ),
+    );
+}
diff --git a/include/client/open.inc.php b/include/client/open.inc.php
index 821aa6ef9cc8661e5126b070dc5d35fedc09d1df..71529516575d3d8b1dbea2d99ac4b7898dea07b4 100644
--- a/include/client/open.inc.php
+++ b/include/client/open.inc.php
@@ -13,11 +13,14 @@ $form = null;
 if (!$info['topicId'])
     $info['topicId'] = $cfg->getDefaultTopicId();
 
+$forms = array();
 if ($info['topicId'] && ($topic=Topic::lookup($info['topicId']))) {
-    $form = $topic->getForm();
-    if ($_POST && $form) {
-        $form = $form->instanciate();
-        $form->isValidForClient();
+    foreach ($topic->getForms() as $F) {
+        if ($_POST) {
+            $F = $F->instanciate();
+            $F->isValidForClient();
+        }
+        $forms[] = $F;
     }
 }
 
@@ -29,9 +32,26 @@ if ($info['topicId'] && ($topic=Topic::lookup($info['topicId']))) {
   <input type="hidden" name="a" value="open">
   <table width="800" cellpadding="1" cellspacing="0" border="0">
     <tbody>
+<?php
+        if (!$thisclient) {
+            $uform = UserForm::getUserForm()->getForm($_POST);
+            if ($_POST) $uform->isValid();
+            $uform->render(false);
+        }
+        else { ?>
+            <tr><td colspan="2"><hr /></td></tr>
+        <tr><td><?php echo __('Email'); ?>:</td><td><?php echo $thisclient->getEmail(); ?></td></tr>
+        <tr><td><?php echo __('Client'); ?>:</td><td><?php echo $thisclient->getName(); ?></td></tr>
+        <?php } ?>
+    </tbody>
+    <tbody>
+    <tr><td colspan="2"><hr />
+        <div class="form-header" style="margin-bottom:0.5em">
+        <b><?php echo __('Help Topic'); ?></b>
+        </div>
+    </td></tr>
     <tr>
-        <td class="required"><?php echo __('Help Topic');?>:</td>
-        <td>
+        <td colspan="2">
             <select id="topicId" name="topicId" onchange="javascript:
                     var data = $(':input[name]', '#dynamic-form').serialize();
                     $.ajax(
@@ -59,28 +79,12 @@ if ($info['topicId'] && ($topic=Topic::lookup($info['topicId']))) {
             <font class="error">*&nbsp;<?php echo $errors['topicId']; ?></font>
         </td>
     </tr>
-<?php
-        if (!$thisclient) {
-            $uform = UserForm::getUserForm()->getForm($_POST);
-            if ($_POST) $uform->isValid();
-            $uform->render(false);
-        }
-        else { ?>
-            <tr><td colspan="2"><hr /></td></tr>
-        <tr><td><?php echo __('Email'); ?>:</td><td><?php echo $thisclient->getEmail(); ?></td></tr>
-        <tr><td><?php echo __('Client'); ?>:</td><td><?php echo $thisclient->getName(); ?></td></tr>
-        <?php } ?>
     </tbody>
     <tbody id="dynamic-form">
-        <?php if ($form) {
+        <?php foreach ($forms as $form) {
             include(CLIENTINC_DIR . 'templates/dynamic-form.tmpl.php');
         } ?>
     </tbody>
-    <tbody><?php
-        $tform = TicketForm::getInstance()->getForm($_POST);
-        if ($_POST) $tform->isValid();
-        $tform->render(false); ?>
-    </tbody>
     <tbody>
     <?php
     if($cfg && $cfg->isCaptchaEnabled() && (!$thisclient || !$thisclient->isValid())) {
diff --git a/include/staff/helptopic.inc.php b/include/staff/helptopic.inc.php
index 6fb6371bc77cd7a61e03ee1830c950cf2ea72504..c9702d2105347d3e47835ac1acc9cd1952c18b8e 100644
--- a/include/staff/helptopic.inc.php
+++ b/include/staff/helptopic.inc.php
@@ -10,6 +10,7 @@ if($topic && $_REQUEST['a']!='add') {
     $info['pid']=$topic->getPid();
     $trans['name'] = $topic->getTranslateTag('name');
     $qs += array('id' => $topic->getId());
+    $forms = $topic->getForms();
 } else {
     $title=__('Add New Help Topic');
     $action='create';
@@ -21,22 +22,31 @@ if($topic && $_REQUEST['a']!='add') {
 }
 $info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
 ?>
+
+<h2 style="font-weight: normal"><?php echo $title; ?>
+    &nbsp;<i class="help-tip icon-question-sign" href="#help_topic_information"></i>
+    </h2>
+<?php if ($topic) { ?>
+    <div class="big"><strong><?php echo $topic->getLocal('topic'); ?></strong></div>
+<?php } ?>
+
+<br/>
+
+<ul class="tabs" id="topic-tabs">
+    <li class="active"><a href="#info"><i class="icon-info-sign"></i> Help Topic Information</a></li>
+    <li><a href="#routing"><i class="icon-ticket"></i> New ticket options</a></li>
+    <li><a href="#forms"><i class="icon-paste"></i> Forms</a></li>
+</ul>
+
 <form action="helptopics.php?<?php echo Http::build_query($qs); ?>" method="post" id="save">
  <?php csrf_token(); ?>
  <input type="hidden" name="do" value="<?php echo $action; ?>">
  <input type="hidden" name="a" value="<?php echo Format::htmlchars($_REQUEST['a']); ?>">
  <input type="hidden" name="id" value="<?php echo $info['id']; ?>">
- <h2><?php echo __('Help Topic');?></h2>
- <table class="form_table" width="940" border="0" cellspacing="0" cellpadding="2">
-    <thead>
-        <tr>
-            <th colspan="2">
-                <h4><?php echo $title; ?></h4>
-                <em><?php echo __('Help Topic Information');?>
-                &nbsp;<i class="help-tip icon-question-sign" href="#help_topic_information"></i></em>
-            </th>
-        </tr>
-    </thead>
+
+<div id="topic-tabs_container">
+<div class="tab_content" id="info">
+ <table class="table" border="0" cellspacing="0" cellpadding="2">
     <tbody>
         <tr>
             <td width="180" class="required">
@@ -53,8 +63,8 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
                 <?php echo __('Status');?>:
             </td>
             <td>
-                <input type="radio" name="isactive" value="1" <?php echo $info['isactive']?'checked="checked"':''; ?>><?php echo __('Active'); ?>
-                <input type="radio" name="isactive" value="0" <?php echo !$info['isactive']?'checked="checked"':''; ?>><?php echo __('Disabled'); ?>
+                <input type="radio" name="isactive" value="1" <?php echo $info['isactive']?'checked="checked"':''; ?>> <?php echo __('Active'); ?>
+                <input type="radio" name="isactive" value="0" <?php echo !$info['isactive']?'checked="checked"':''; ?>> <?php echo __('Disabled'); ?>
                 &nbsp;<span class="error">*&nbsp;</span> <i class="help-tip icon-question-sign" href="#status"></i>
             </td>
         </tr>
@@ -63,8 +73,8 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
                 <?php echo __('Type');?>:
             </td>
             <td>
-                <input type="radio" name="ispublic" value="1" <?php echo $info['ispublic']?'checked="checked"':''; ?>><?php echo __('Public'); ?>
-                <input type="radio" name="ispublic" value="0" <?php echo !$info['ispublic']?'checked="checked"':''; ?>><?php echo __('Private/Internal'); ?>
+                <input type="radio" name="ispublic" value="1" <?php echo $info['ispublic']?'checked="checked"':''; ?>> <?php echo __('Public'); ?>
+                <input type="radio" name="ispublic" value="0" <?php echo !$info['ispublic']?'checked="checked"':''; ?>> <?php echo __('Private/Internal'); ?>
                 &nbsp;<span class="error">*&nbsp;</span> <i class="help-tip icon-question-sign" href="#type"></i>
             </td>
         </tr>
@@ -87,28 +97,26 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
             </td>
         </tr>
 
-        <tr><th colspan="2"><em><?php echo __('New ticket options');?></em></th></tr>
-        <tr>
-            <td><strong><?php echo __('Custom Form'); ?></strong>:</td>
-           <td><select name="form_id">
-                <option value="0" <?php
-if ($info['form_id'] == '0') echo 'selected="selected"';
-                    ?>>&mdash; <?php echo __('None'); ?> &mdash;</option>
-                <option value="<?php echo Topic::FORM_USE_PARENT; ?>"  <?php
-if ($info['form_id'] == Topic::FORM_USE_PARENT) echo 'selected="selected"';
-                    ?>>&mdash; <?php echo __('Use Parent Form'); ?> &mdash;</option>
-               <?php foreach (DynamicForm::objects()->filter(array('type'=>'G')) as $group) { ?>
-                <option value="<?php echo $group->get('id'); ?>"
-                       <?php if ($group->get('id') == $info['form_id'])
-                            echo 'selected="selected"'; ?>>
-                       <?php echo $group->get('title'); ?>
-                   </option>
-               <?php } ?>
-               </select>
-               &nbsp;<span class="error">&nbsp;<?php echo $errors['form_id']; ?></span>
-               <i class="help-tip icon-question-sign" href="#custom_form"></i>
-           </td>
-        </tr>
+    </tbody>
+    </table>
+
+        <div style="padding:8px 3px;border-bottom: 2px dotted #ddd;">
+            <strong class="big"><?php echo __('Internal Notes');?></strong><br/>
+            <?php echo __("be liberal, they're internal.");?>
+        </div>
+
+        <textarea class="richtext no-bar" name="notes" cols="21"
+            rows="8" style="width: 80%;"><?php echo $info['notes']; ?></textarea>
+
+</div>
+
+<div class="hidden tab_content" id="routing">
+<div style="padding:8px 0;border-bottom: 2px dotted #ddd;">
+<div><b class="big"><?php echo __('New ticket options');?></b></div>
+</div>
+
+ <table class="table" border="0" cellspacing="0" cellpadding="2">
+        <tbody>
         <tr>
             <td width="180" class="required">
                 <?php echo __('Department'); ?>:
@@ -127,6 +135,62 @@ if ($info['form_id'] == Topic::FORM_USE_PARENT) echo 'selected="selected"';
                 <i class="help-tip icon-question-sign" href="#department"></i>
             </td>
         </tr>
+        <tr class="border">
+            <td>
+                <?php echo __('Ticket Number Format'); ?>:
+            </td>
+            <td>
+                <label>
+                <input type="radio" name="custom-numbers" value="0" <?php echo !$info['custom-numbers']?'checked="checked"':''; ?>
+                    onchange="javascript:$('#custom-numbers').hide();"> <?php echo __('System Default'); ?>
+                </label>&nbsp;<label>
+                <input type="radio" name="custom-numbers" value="1" <?php echo $info['custom-numbers']?'checked="checked"':''; ?>
+                    onchange="javascript:$('#custom-numbers').show(200);"> <?php echo __('Custom'); ?>
+                </label>&nbsp; <i class="help-tip icon-question-sign" href="#custom_numbers"></i>
+            </td>
+        </tr>
+    </tbody>
+    <tbody id="custom-numbers" style="<?php if (!$info['custom-numbers']) echo 'display:none'; ?>">
+        <tr>
+            <td style="padding-left:20px">
+                <?php echo __('Format'); ?>:
+            </td>
+            <td>
+                <input type="text" name="number_format" value="<?php echo $info['number_format']; ?>"/>
+                <span class="faded"><?php echo __('e.g.'); ?> <span id="format-example"><?php
+                    if ($info['custom-numbers']) {
+                        if ($info['sequence_id'])
+                            $seq = Sequence::lookup($info['sequence_id']);
+                        if (!isset($seq))
+                            $seq = new RandomSequence();
+                        echo $seq->current($info['number_format']);
+                    } ?></span></span>
+                <div class="error"><?php echo $errors['number_format']; ?></div>
+            </td>
+        </tr>
+        <tr>
+<?php $selected = 'selected="selected"'; ?>
+            <td style="padding-left:20px">
+                <?php echo __('Sequence'); ?>:
+            </td>
+            <td>
+                <select name="sequence_id">
+                <option value="0" <?php if ($info['sequence_id'] == 0) echo $selected;
+                    ?>>&mdash; <?php echo __('Random'); ?> &mdash;</option>
+<?php foreach (Sequence::objects() as $s) { ?>
+                <option value="<?php echo $s->id; ?>" <?php
+                    if ($info['sequence_id'] == $s->id) echo $selected;
+                    ?>><?php echo $s->name; ?></option>
+<?php } ?>
+                </select>
+                <button class="action-button pull-right" onclick="javascript:
+                $.dialog('ajax.php/sequence/manage', 205);
+                return false;
+                "><i class="icon-gear"></i> <?php echo __('Manage'); ?></button>
+            </td>
+        </tr>
+    </tbody>
+    <tbody>
         <tr>
             <td width="180">
                 <?php echo __('Status'); ?>:
@@ -266,75 +330,90 @@ if ($info['form_id'] == Topic::FORM_USE_PARENT) echo 'selected="selected"';
                     <i class="help-tip icon-question-sign" href="#ticket_auto_response"></i>
             </td>
         </tr>
-        <tr>
-            <td>
-                <?php echo __('Ticket Number Format'); ?>:
-            </td>
-            <td>
-                <label>
-                <input type="radio" name="custom-numbers" value="0" <?php echo !$info['custom-numbers']?'checked="checked"':''; ?>
-                    onchange="javascript:$('#custom-numbers').hide();"> <?php echo __('System Default'); ?>
-                </label>&nbsp;<label>
-                <input type="radio" name="custom-numbers" value="1" <?php echo $info['custom-numbers']?'checked="checked"':''; ?>
-                    onchange="javascript:$('#custom-numbers').show(200);"> <?php echo __('Custom'); ?>
-                </label>&nbsp; <i class="help-tip icon-question-sign" href="#custom_numbers"></i>
-            </td>
-        </tr>
     </tbody>
-    <tbody id="custom-numbers" style="<?php if (!$info['custom-numbers']) echo 'display:none'; ?>">
-        <tr>
-            <td style="padding-left:20px">
-                <?php echo __('Format'); ?>:
-            </td>
-            <td>
-                <input type="text" name="number_format" value="<?php echo $info['number_format']; ?>"/>
-                <span class="faded"><?php echo __('e.g.'); ?> <span id="format-example"><?php
-                    if ($info['custom-numbers']) {
-                        if ($info['sequence_id'])
-                            $seq = Sequence::lookup($info['sequence_id']);
-                        if (!isset($seq))
-                            $seq = new RandomSequence();
-                        echo $seq->current($info['number_format']);
-                    } ?></span></span>
-                <div class="error"><?php echo $errors['number_format']; ?></div>
-            </td>
-        </tr>
+ </table>
+</div>
+
+<div class="hidden tab_content" id="forms">
+ <table id="topic-forms" class="table" border="0" cellspacing="0" cellpadding="2">
+
+<?php foreach ($forms as $F) { ?>
+    <tbody data-form-id="<?php echo $F->get('id'); ?>">
         <tr>
-<?php $selected = 'selected="selected"'; ?>
-            <td style="padding-left:20px">
-                <?php echo __('Sequence'); ?>:
-            </td>
-            <td>
-                <select name="sequence_id">
-                <option value="0" <?php if ($info['sequence_id'] == 0) echo $selected;
-                    ?>>&mdash; <?php echo __('Random'); ?> &mdash;</option>
-<?php foreach (Sequence::objects() as $s) { ?>
-                <option value="<?php echo $s->id; ?>" <?php
-                    if ($info['sequence_id'] == $s->id) echo $selected;
-                    ?>><?php echo $s->name; ?></option>
+            <td class="handle" colspan="6">
+                <input type="hidden" name="forms[]" value="<?php echo $F->get('id'); ?>" />
+                <div class="pull-right">
+                <i class="icon-2x icon-move icon-muted"></i>
+<?php if ($F->get('type') != 'T') { ?>
+                <a href="#" title="<?php echo __('Delete'); ?>" onclick="javascript:
+                if (confirm(__('You sure?')))
+                    var tbody = $(this).closest('tbody');
+                    tbody.fadeOut(function(){this.remove()});
+                    $(this).closest('form')
+                        .find('[name=form_id] [value=' + tbody.data('formId') + ']')
+                        .prop('disabled', false);
+                return false;"><i class="icon-2x icon-trash"></i></a>
 <?php } ?>
-                </select>
-                <button class="action-button pull-right" onclick="javascript:
-                $.dialog('ajax.php/sequence/manage', 205);
-                return false;
-                "><i class="icon-gear"></i> <?php echo __('Manage'); ?></button>
+                </div>
+                <div><strong><?php echo $F->getLocal('title'); ?></strong></div>
+                <div><?php echo $F->getLocal('instructions'); ?></div>
             </td>
         </tr>
-    </tbody>
-    <tbody>
         <tr>
-            <th colspan="2">
-                <em><strong><?php echo __('Internal Notes');?></strong>: <?php echo __("be liberal, they're internal.");?></em>
-            </th>
+            <th><?php echo __('Enable'); ?></th>
+            <th><?php echo __('Label'); ?></th>
+            <th><?php echo __('Type'); ?></th>
+            <th><?php echo __('Visibility'); ?></th>
+            <th><?php echo __('Variable'); ?></th>
         </tr>
+    <?php
+        foreach ($F->getFields() as $f) { ?>
         <tr>
-            <td colspan=2>
-                <textarea class="richtext no-bar" name="notes" cols="21"
-                    rows="8" style="width: 80%;"><?php echo $info['notes']; ?></textarea>
-            </td>
+            <td><input type="checkbox" name="fields[]" value="<?php
+                echo $f->get('id'); ?>" <?php
+                if ($f->isEnabled()) echo 'checked="checked"'; ?>/></td>
+            <td><?php echo $f->get('label'); ?></td>
+            <td><?php $t=FormField::getFieldType($f->get('type')); echo __($t[0]); ?></td>
+            <td><?php echo $f->getVisibilityDescription(); ?></td>
+            <td><?php echo $f->get('name'); ?></td>
         </tr>
+        <?php } ?>
     </tbody>
-</table>
+    <?php } ?>
+ </table>
+
+   <br/>
+   <strong><?php echo __('Add Custom Form'); ?></strong>:
+   <select name="form_id" onchange="javascript:
+    event.preventDefault();
+    var $this = $(this),
+        val = $this.val();
+    if (!val) return;
+    $.ajax({
+        url: 'ajax.php/form/' + val + '/fields/view',
+        dataType: 'json',
+        success: function(json) {
+            if (json.success) {
+                $(json.html).appendTo('#topic-forms').effect('highlight');
+                $this.find(':selected').prop('disabled', true);
+            }
+        }
+    });">
+    <option value=""><?php echo '— '.__('Add a custom form') . ' —'; ?></option>
+    <?php foreach (DynamicForm::objects()->filter(array('type'=>'G')) as $F) { ?>
+        <option value="<?php echo $F->get('id'); ?>"
+           <?php if ($F->get('id') == $info['form_id'])
+                echo 'selected="selected"'; ?>>
+           <?php echo $F->getLocal('title'); ?>
+        </option>
+    <?php } ?>
+   </select>
+   &nbsp;<span class="error">&nbsp;<?php echo $errors['form_id']; ?></span>
+   <i class="help-tip icon-question-sign" href="#custom_form"></i>
+</div>
+
+</div>
+
 <p style="text-align:center;">
     <input type="submit" name="submit" value="<?php echo $submit_text; ?>">
     <input type="reset"  name="reset"  value="<?php echo __('Reset');?>">
@@ -355,4 +434,19 @@ $(function() {
     $('[name=sequence_id]').on('change', update_example);
     $('[name=number_format]').on('keyup', update_example);
 });
+$('table#topic-forms').sortable({
+  items: 'tbody',
+  handle: 'td.handle',
+  tolerance: 'pointer',
+  forcePlaceholderSize: true,
+  helper: function(e, ui) {
+    ui.children().each(function() {
+      $(this).children().each(function() {
+        $(this).width($(this).width());
+      });
+    });
+    ui=ui.clone().css({'background-color':'white', 'opacity':0.8});
+    return ui;
+  }
+});
 </script>
diff --git a/include/staff/templates/dynamic-form.tmpl.php b/include/staff/templates/dynamic-form.tmpl.php
index 487ce9a56f863a735abe6808f29a95c56bf2dc9f..6fc7b8978faea9b538a80e55acc6db3b503a0434 100644
--- a/include/staff/templates/dynamic-form.tmpl.php
+++ b/include/staff/templates/dynamic-form.tmpl.php
@@ -38,6 +38,8 @@ if (isset($options['entry']) && $options['mode'] == 'edit') { ?>
     }
     foreach ($form->getFields() as $field) {
         try {
+            if (!$field->isEnabled())
+                continue;
             if ($options['mode'] == 'edit' && !$field->isEditableToStaff())
                 continue;
         }
diff --git a/include/staff/ticket-open.inc.php b/include/staff/ticket-open.inc.php
index fcebc13d474f66469e7eb2e188f55505de61e165..50750cdd0df2c3a7c5d64ea88c83e86bba5c67a7 100644
--- a/include/staff/ticket-open.inc.php
+++ b/include/staff/ticket-open.inc.php
@@ -9,12 +9,14 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
 if (!$info['topicId'])
     $info['topicId'] = $cfg->getDefaultTopicId();
 
-$form = null;
+$forms = array();
 if ($info['topicId'] && ($topic=Topic::lookup($info['topicId']))) {
-    $form = $topic->getForm();
-    if ($_POST && $form) {
-        $form = $form->instanciate();
-        $form->isValid();
+    foreach ($topic->getForms() as $F) {
+        if ($_POST) {
+            $F = $F->instanciate();
+            $F->isValidForClient();
+        }
+        $forms[] = $F;
     }
 }
 
@@ -156,9 +158,9 @@ if ($_POST)
                                 $id, ($info['topicId']==$id)?'selected="selected"':'',
                                 $selected, $name);
                         }
-                        if (count($topics) == 1 && !$form) {
+                        if (count($topics) == 1 && !$forms) {
                             if (($T = Topic::lookup($id)))
-                                $form =  $T->getForm();
+                                $forms =  $T->getForms();
                         }
                     }
                     ?>
@@ -260,18 +262,12 @@ if ($_POST)
         </tbody>
         <tbody id="dynamic-form">
         <?php
-            if ($form) {
+            foreach ($forms as $form) {
                 print $form->getForm()->getMedia();
                 include(STAFFINC_DIR .  'templates/dynamic-form.tmpl.php');
             }
         ?>
         </tbody>
-        <tbody> <?php
-        $tform = TicketForm::getInstance()->getForm($_POST);
-        if ($_POST) $tform->isValid();
-        $tform->render(true);
-        ?>
-        </tbody>
         <tbody>
         <?php
         //is the user allowed to post replies??
diff --git a/include/upgrader/streams/core/5cd0a25a-00000000.cleanup.sql b/include/upgrader/streams/core/5cd0a25a-00000000.cleanup.sql
new file mode 100644
index 0000000000000000000000000000000000000000..3b391fc4d5cab0d4c779c4d380ae769e2d67ce3d
--- /dev/null
+++ b/include/upgrader/streams/core/5cd0a25a-00000000.cleanup.sql
@@ -0,0 +1,8 @@
+/**
+ * @signature 0e47d678f50874fa0d33e1e3759f657e
+ * @version v1.9.6
+ * @title Make fields disable-able per help topic
+ */
+
+ALTER TABLE `%TABLE_PREFIX%help_topic`
+    DROP `form_id` int(10) unsigned NOT NULL default '0';
diff --git a/include/upgrader/streams/core/5cd0a25a-00000000.patch.sql b/include/upgrader/streams/core/5cd0a25a-00000000.patch.sql
new file mode 100644
index 0000000000000000000000000000000000000000..58e4fdfc2fd7e139cb432937efbd0fe4f318249c
--- /dev/null
+++ b/include/upgrader/streams/core/5cd0a25a-00000000.patch.sql
@@ -0,0 +1,45 @@
+/**
+ * @signature 0e47d678f50874fa0d33e1e3759f657e
+ * @version v1.9.6
+ * @title Make fields disable-able per help topic
+ */
+
+ALTER TABLE `%TABLE_PREFIX%form`
+    ADD `pid` int(10) unsigned DEFAULT NULL AFTER `id`,
+    ADD `name` varchar(64) NOT NULL DEFAULT '' AFTER `instructions`;
+
+ALTER TABLE `%TABLE_PREFIX%form_entry`
+    ADD `extra` text AFTER `sort`;
+
+CREATE TABLE `%TABLE_PREFIX%help_topic_form` (
+  `id` int(11) unsigned NOT NULL auto_increment,
+  `topic_id` int(11) unsigned NOT NULL default 0,
+  `form_id` int(10) unsigned NOT NULL default 0,
+  `sort` int(10) unsigned NOT NULL default 1,
+  `extra` text,
+  PRIMARY KEY  (`topic_id`, `form_id`)
+) DEFAULT CHARSET=utf8;
+
+insert into `%TABLE_PREFIX%help_topic_form`
+    (`topic_id`, `form_id`, `sort`)
+    select A1.topic_id, case
+        when A2.form_id = 4294967295 then A3.form_id
+        when A1.form_id = 4294967295 then A2.form_id
+        else A1.form_id end as form_id, 1 as `sort`
+    from `%TABLE_PREFIX%help_topic` A1
+    left join `%TABLE_PREFIX%help_topic` A2 on (A2.topic_pid = A1.topic_id)
+    left join `%TABLE_PREFIX%help_topic` A3 on (A3.topic_pid = A2.topic_id)
+    having `form_id` > 0
+    union
+    select A2.topic_id, id as `form_id`, 2 as `sort`
+    from `%TABLE_PREFIX%form` A1
+    join `%TABLE_PREFIX%help_topic` A2
+    where A1.`type` = 'T';
+
+ALTER TABLE `%TABLE_PREFIX%help_topic`
+    DROP `form_id` int(10) unsigned NOT NULL default '0';
+
+-- Finished with patch
+UPDATE `%TABLE_PREFIX%config`
+    SET `value` = '0e47d678f50874fa0d33e1e3759f657e'
+    WHERE `key` = 'schema_signature' AND `namespace` = 'core';
diff --git a/scp/ajax.php b/scp/ajax.php
index 131243adeb18c39b487631bcb9c5aee7b50044c1..79dd8e1b384456df05a05373c4887f99cd7d0098 100644
--- a/scp/ajax.php
+++ b/scp/ajax.php
@@ -57,7 +57,8 @@ $dispatcher = patterns('',
         url_post('^field-config/(?P<id>\d+)$', 'saveFieldConfiguration'),
         url_delete('^answer/(?P<entry>\d+)/(?P<field>\d+)$', 'deleteAnswer'),
         url_post('^upload/(\d+)?$', 'upload'),
-        url_post('^upload/(\w+)?$', 'attach')
+        url_post('^upload/(\w+)?$', 'attach'),
+        url_get('^(?P<id>\d+)/fields/view$', 'getAllFields')
     )),
     url('^/list/', patterns('ajax.forms.php:DynamicFormsAjaxAPI',
         url_get('^(?P<list>\w+)/item/(?P<id>\d+)/properties$', 'getListItemProperties'),
diff --git a/scp/css/scp.css b/scp/css/scp.css
index 0d76bf2ba467afd9ff21f10e99c9f039bdc3fb2e..4773680839f8c5f349e6c2fc6090ef7a8b803464 100644
--- a/scp/css/scp.css
+++ b/scp/css/scp.css
@@ -51,6 +51,10 @@ div#header a {
     clear:both;
 }
 
+.big {
+    font-size: 110%;
+}
+
 .faded {
     color:#666;
 }
@@ -589,6 +593,27 @@ a.print {
     padding:2px;
 }
 
+.table {
+    width: 100%;
+    border-collapse: collapse;
+    margin-top:3px;
+}
+
+.table tr.header td,
+.table th {
+    font-weight: bold;
+    text-align: left;
+    height: 24px;
+    background: #f0f0f0;
+}
+
+.table tr {
+    border-bottom:1px dotted #ddd;
+}
+.table td:not(:empty) {
+    height: 24px;
+}
+
 .form_table {
     margin-top:3px;
     border-left:1px solid #ddd;
diff --git a/setup/inc/streams/core/install-mysql.sql b/setup/inc/streams/core/install-mysql.sql
index 124b65afbb7316e046f27a427bd569048d25a17f..738df56ce9521c2a087d5aa04f326b89cff14f0d 100644
--- a/setup/inc/streams/core/install-mysql.sql
+++ b/setup/inc/streams/core/install-mysql.sql
@@ -115,10 +115,12 @@ INSERT INTO `%TABLE_PREFIX%config` (`namespace`, `key`, `value`) VALUES
 DROP TABLE IF EXISTS `%TABLE_PREFIX%form`;
 CREATE TABLE `%TABLE_PREFIX%form` (
     `id` int(11) unsigned NOT NULL auto_increment,
+    `pid` int(10) unsigned DEFAULT NULL,
     `type` varchar(8) NOT NULL DEFAULT 'G',
     `deletable` tinyint(1) NOT NULL DEFAULT 1,
     `title` varchar(255) NOT NULL,
     `instructions` varchar(512),
+    `name` varchar(64) NOT NULL DEFAULT '',
     `notes` text,
     `created` datetime NOT NULL,
     `updated` datetime NOT NULL,
@@ -151,6 +153,7 @@ CREATE TABLE `%TABLE_PREFIX%form_entry` (
     `object_id` int(11) unsigned,
     `object_type` char(1) NOT NULL DEFAULT 'T',
     `sort` int(11) unsigned NOT NULL DEFAULT 1,
+    `extra` text,
     `created` datetime NOT NULL,
     `updated` datetime NOT NULL,
     PRIMARY KEY (`id`),
@@ -443,7 +446,6 @@ CREATE TABLE `%TABLE_PREFIX%help_topic` (
   `team_id` int(10) unsigned NOT NULL default '0',
   `sla_id` int(10) unsigned NOT NULL default '0',
   `page_id` int(10) unsigned NOT NULL default '0',
-  `form_id` int(10) unsigned NOT NULL default '0',
   `sequence_id` int(10) unsigned NOT NULL DEFAULT '0',
   `sort` int(10) unsigned NOT NULL default '0',
   `topic` varchar(32) NOT NULL default '',
@@ -461,6 +463,16 @@ CREATE TABLE `%TABLE_PREFIX%help_topic` (
   KEY `page_id` (`page_id`)
 ) DEFAULT CHARSET=utf8;
 
+DROP TABLE IF EXISTS `%TABLE_PREFIX%help_topic_form`;
+CREATE TABLE `%TABLE_PREFIX%help_topic_form` (
+  `id` int(11) unsigned NOT NULL auto_increment,
+  `topic_id` int(11) unsigned NOT NULL default 0,
+  `form_id` int(10) unsigned NOT NULL default 0,
+  `sort` int(10) unsigned NOT NULL default 1,
+  `extra` text,
+  PRIMARY KEY  (`topic_id`, `form_id`)
+) DEFAULT CHARSET=utf8;
+
 DROP TABLE IF EXISTS `%TABLE_PREFIX%organization`;
 CREATE TABLE `%TABLE_PREFIX%organization` (
   `id` int(11) unsigned NOT NULL AUTO_INCREMENT,