diff --git a/css/osticket.css b/css/osticket.css
index 87e6c6aed4b382095e42199f41a00f35bea32621..8b986fef422772ae58c24cbaed324763e8171c03 100644
--- a/css/osticket.css
+++ b/css/osticket.css
@@ -43,3 +43,10 @@
     display: block;
     opacity: 0.3;
 }
+
+div.section-break {
+    margin-top: 1em;
+    margin-bottom: 0.5em;
+    padding-top: 0.8em !important;
+    border-top: 1px solid #ccc;
+}
diff --git a/include/ajax.forms.php b/include/ajax.forms.php
index ac49c8cdb053f0b055f888cd2d74c1cfb5dabca5..17cae9189bfcb7c049c0f0bd53977e41612d2c1c 100644
--- a/include/ajax.forms.php
+++ b/include/ajax.forms.php
@@ -15,12 +15,8 @@ class DynamicFormsAjaxAPI extends AjaxController {
 
     function getFormsForHelpTopic($topic_id, $client=false) {
         $topic = Topic::lookup($topic_id);
-        $form =DynamicForm::lookup($topic->ht['form_id']);
-        $set=$form;
-        if ($client)
-            include(CLIENTINC_DIR . 'templates/dynamic-form.tmpl.php');
-        else
-            include(STAFFINC_DIR . 'templates/dynamic-form.tmpl.php');
+        if ($form =DynamicForm::lookup($topic->ht['form_id']))
+            $form->render(!$client);
     }
 
     function getClientFormsForHelpTopic($topic_id) {
@@ -66,8 +62,10 @@ class DynamicFormsAjaxAPI extends AjaxController {
         $static->data($data);
 
         $custom = array();
-        foreach ($user->getDynamicData() as $cd)
+        foreach ($user->getDynamicData() as $cd) {
+            $cd->addMissingFields();
             $custom[] = $cd->getForm();
+        }
 
         include(STAFFINC_DIR . 'templates/user-info.tmpl.php');
     }
@@ -80,8 +78,9 @@ class DynamicFormsAjaxAPI extends AjaxController {
         $custom_data = $user->getDynamicData();
         $custom = array();
         foreach ($custom_data as $cd) {
+            $cd->addMissingFields();
             $cf = $custom[] = $cd->getForm();
-            $valid &= $cf->isValid();
+            $valid &= $cd->isValid();
         }
 
         if (!$valid) {
diff --git a/include/ajax.tickets.php b/include/ajax.tickets.php
index 771dcf7f6ba2270a1ab3db6183da77b957c68b84..c664466fb1ad9330826d7029081bbac8da386308 100644
--- a/include/ajax.tickets.php
+++ b/include/ajax.tickets.php
@@ -17,6 +17,7 @@
 if(!defined('INCLUDE_DIR')) die('403');
 
 include_once(INCLUDE_DIR.'class.ticket.php');
+require_once(INCLUDE_DIR.'class.ajax.php');
 
 class TicketsAjaxAPI extends AjaxController {
 
@@ -77,7 +78,7 @@ class TicketsAjaxAPI extends AjaxController {
             $sql.=' OR dept_id IN ('.implode(',', db_input($depts)).')';
 
         $sql.=' ) '
-            .' GROUP BY email.value '
+            .' GROUP BY email.address '
             .' ORDER BY ticket.created  LIMIT '.$limit;
 
         if(($res=db_query($sql)) && db_num_rows($res)) {
@@ -88,11 +89,11 @@ class TicketsAjaxAPI extends AjaxController {
         return $this->json_encode($tickets);
     }
 
-    function search() {
+    function _search($req) {
         global $thisstaff, $cfg;
 
         $result=array();
-        $select = 'SELECT count( DISTINCT ticket.ticket_id) as tickets ';
+        $select = 'SELECT DISTINCT ticket.ticket_id';
         $from = ' FROM '.TICKET_TABLE.' ticket ';
         $where = ' WHERE 1 ';
 
@@ -108,15 +109,15 @@ class TicketsAjaxAPI extends AjaxController {
         $where.=' ) ';
 
         //Department
-        if($_REQUEST['deptId'])
-            $where.=' AND ticket.dept_id='.db_input($_REQUEST['deptId']);
+        if($req['deptId'])
+            $where.=' AND ticket.dept_id='.db_input($req['deptId']);
 
         //Help topic
-        if($_REQUEST['topicId'])
-            $where.=' AND ticket.topic_id='.db_input($_REQUEST['topicId']);
+        if($req['topicId'])
+            $where.=' AND ticket.topic_id='.db_input($req['topicId']);
 
         //Status
-        switch(strtolower($_REQUEST['status'])) {
+        switch(strtolower($req['status'])) {
             case 'open':
                 $where.=' AND ticket.status="open" ';
                 break;
@@ -132,9 +133,9 @@ class TicketsAjaxAPI extends AjaxController {
         }
 
         //Assignee
-        if(isset($_REQUEST['assignee']) && strcasecmp($_REQUEST['status'], 'closed'))  {
-            $id=preg_replace("/[^0-9]/", "", $_REQUEST['assignee']);
-            $assignee = $_REQUEST['assignee'];
+        if(isset($req['assignee']) && strcasecmp($req['status'], 'closed'))  {
+            $id=preg_replace("/[^0-9]/", "", $req['assignee']);
+            $assignee = $req['assignee'];
             $where.= ' AND ( ( ticket.status="open" ';
             if($assignee[0]=='t')
                 $where.=' AND ticket.team_id='.db_input($id);
@@ -145,19 +146,19 @@ class TicketsAjaxAPI extends AjaxController {
 
             $where.=')';
 
-            if($_REQUEST['staffId'] && !$_REQUEST['status']) //Assigned TO + Closed By
-                $where.= ' OR (ticket.staff_id='.db_input($_REQUEST['staffId']). ' AND ticket.status="closed") ';
-            elseif(isset($_REQUEST['staffId'])) // closed by any
+            if($req['staffId'] && !$req['status']) //Assigned TO + Closed By
+                $where.= ' OR (ticket.staff_id='.db_input($req['staffId']). ' AND ticket.status="closed") ';
+            elseif(isset($req['staffId'])) // closed by any
                 $where.= ' OR ticket.status="closed" ';
 
             $where.= ' ) ';
-        } elseif($_REQUEST['staffId']) {
-            $where.=' AND (ticket.staff_id='.db_input($_REQUEST['staffId']).' AND ticket.status="closed") ';
+        } elseif($req['staffId']) {
+            $where.=' AND (ticket.staff_id='.db_input($req['staffId']).' AND ticket.status="closed") ';
         }
 
         //dates
-        $startTime  =($_REQUEST['startDate'] && (strlen($_REQUEST['startDate'])>=8))?strtotime($_REQUEST['startDate']):0;
-        $endTime    =($_REQUEST['endDate'] && (strlen($_REQUEST['endDate'])>=8))?strtotime($_REQUEST['endDate']):0;
+        $startTime  =($req['startDate'] && (strlen($req['startDate'])>=8))?strtotime($req['startDate']):0;
+        $endTime    =($req['endDate'] && (strlen($req['endDate'])>=8))?strtotime($req['endDate']):0;
         if( ($startTime && $startTime>time()) or ($startTime>$endTime && $endTime>0))
             $startTime=$endTime=0;
 
@@ -168,23 +169,65 @@ class TicketsAjaxAPI extends AjaxController {
             $where.=' AND ticket.created<=FROM_UNIXTIME('.$endTime.')';
 
         //Query
-        if($_REQUEST['query']) {
-            $queryterm=db_real_escape($_REQUEST['query'], false);
-
-            $from.=' LEFT JOIN '.TICKET_THREAD_TABLE.' thread ON (ticket.ticket_id=thread.ticket_id )';
-            $where.=" AND (  ticket.email LIKE '%$queryterm%'"
-                       ." OR ticket.name LIKE '%$queryterm%'"
-                       ." OR ticket.subject LIKE '%$queryterm%'"
+        if($req['query']) {
+            $queryterm=db_real_escape($req['query'], false);
+
+            $from.=' LEFT JOIN '.TICKET_THREAD_TABLE.' thread ON (ticket.ticket_id=thread.ticket_id )'
+                .' LEFT JOIN '.FORM_ENTRY_TABLE.' tentry ON (tentry.object_id = ticket.ticket_id
+                   AND tentry.object_type="T")
+                   LEFT JOIN '.FORM_ANSWER_TABLE.' tans ON (tans.entry_id = tentry.id
+                   AND tans.value_id IS NULL)
+                   LEFT JOIN '.FORM_ENTRY_TABLE.' uentry ON (uentry.object_id = ticket.user_id
+                   AND uentry.object_type="U")
+                   LEFT JOIN '.FORM_ANSWER_TABLE.' uans ON (uans.entry_id = uentry.id
+                   AND uans.value_id IS NULL)
+                   LEFT JOIN '.USER_TABLE.' user ON (ticket.user_id = user.id)
+                   LEFT JOIN '.USER_EMAIL_TABLE.' uemail ON (user.id = uemail.user_id)';
+
+            $where.=" AND (  uemail.address LIKE '%$queryterm%'"
+                       ." OR user.name LIKE '%$queryterm%'"
+                       ." OR tans.value LIKE '%$queryterm%'"
+                       ." OR uans.value LIKE '%$queryterm%'"
                        ." OR thread.title LIKE '%$queryterm%'"
                        ." OR thread.body LIKE '%$queryterm%'"
                        .' )';
         }
 
+        // Dynamic fields
+        $dynfields='(SELECT entry.object_id, value, value_id FROM '.FORM_ANSWER_TABLE.' ans '.
+             'LEFT JOIN '.FORM_ENTRY_TABLE.' entry ON entry.id=ans.entry_id '.
+             'LEFT JOIN '.FORM_FIELD_TABLE.' field ON field.id=ans.field_id '.
+             'WHERE field.name = %1$s AND entry.object_type="T")';
+        foreach (TicketForm::getInstance()->getFields() as $f) {
+            if ($f->get('name') && isset($req[$f->getFormName()])
+                    && ($val = $req[$f->getFormName()])) {
+                $name = 'dyn_'.$f->get('id');
+                $from .= ' LEFT JOIN '.sprintf($dynfields, db_input($f->get('name')))
+                    ." $name ON ($name.object_id = ticket.ticket_id)";
+                $where .= " AND ($name.value_id = ".db_input($val)
+                    . " OR $name.value LIKE '%".db_real_escape($val)."%')";
+            }
+        }
+
         $sql="$select $from $where";
-        if(($tickets=db_result(db_query($sql)))) {
-            $result['success'] =sprintf("Search criteria matched %s - <a href='tickets.php?%s'>view</a>",
-                                        ($tickets>1?"$tickets tickets":"$tickets ticket"),
-                                        str_replace(array('&amp;', '&'), array('&', '&amp;'), $_SERVER['QUERY_STRING']));
+        $res = db_query($sql);
+        while (list($tickets[]) = db_fetch_row($res));
+        $tickets = array_filter($tickets);
+
+        return $tickets;
+    }
+
+    function search() {
+        $tickets = self::_search($_REQUEST);
+
+        if (count($tickets)) {
+            $uid = md5($_SERVER['QUERY_STRING']);
+            $_SESSION["adv_$uid"] = $tickets;
+            $result['success'] =sprintf(
+                "Search criteria matched %d %s - <a href='tickets.php?%s'>view</a>",
+                count($tickets), (count($tickets)>1?"tickets":"ticket"),
+                'advsid='.$uid
+            );
         } else {
             $result['fail']='No tickets found matching your search criteria.';
         }
diff --git a/include/api.tickets.php b/include/api.tickets.php
index b57f65125ec050cb0dff0f40f0c5d507476bd01c..81dac7a927134e11e090cc1e356b42e82064b915 100644
--- a/include/api.tickets.php
+++ b/include/api.tickets.php
@@ -21,9 +21,12 @@ class TicketApiController extends ApiController {
         if (isset($data['topicId'])) {
             $topic=Topic::lookup($data['topicId']);
             $form=DynamicForm::lookup($topic->ht['form_id']);
-            foreach ($form->getFields() as $field)
+            foreach ($form->getDynamicFields() as $field)
                 $supported[] = $field->get('name');
         }
+        $form = TicketForm::lookup()->instanciate();
+        foreach ($form->getDynamicFields() as $field)
+            $supported[] = $field->get('name');
 
         if(!strcasecmp($format, 'email')) {
             $supported = array_merge($supported, array('header', 'mid',
@@ -98,16 +101,13 @@ class TicketApiController extends ApiController {
         # Create the ticket with the data (attempt to anyway)
         $errors = array();
 
-        $topic=Topic::lookup($data['topicId']);
-        $form=DynamicForm::lookup($topic->ht['form_id'])->instanciate();
-        # Collect name, email address, and subject for banning and such
-        foreach ($form->getFields() as $field) {
-            $fname = $field->get('name');
-            if ($fname && isset($data[$fname]))
-                $field->value = $data[$fname];
+        if ($topic=Topic::lookup($data['topicId'])) {
+            if ($form=DynamicForm::lookup($topic->ht['form_id'])) {
+                $form = $form->instanciate();
+                if (!$form->isValid())
+                    $errors += $form->errors();
+            }
         }
-        if (!$form->isValid())
-            $errors = array_merge($errors, $form->errors());
 
         $ticket = Ticket::create($data, $errors, $data['source'], $autorespond, $alert);
         # Return errors (?)
@@ -124,10 +124,10 @@ class TicketApiController extends ApiController {
             return $this->exerr(500, "Unable to create new ticket: unknown error");
         }
 
-        # Save dynamic forms
-        foreach ($forms as $f) {
-            $f->setTicketId($ticket->getId());
-            $f->save();
+        # Save dynamic form
+        if (isset($form)) {
+            $form->setTicketId($ticket->getId());
+            $form->save();
         }
 
         return $ticket;
diff --git a/include/class.dynamic_forms.php b/include/class.dynamic_forms.php
index 0418bab1e1f3f966464d987d656476ed8f1c5c4d..f38f09bf6f5a6cafd917e2ef7ec9ee7dfbdcbb7d 100644
--- a/include/class.dynamic_forms.php
+++ b/include/class.dynamic_forms.php
@@ -45,7 +45,8 @@ class DynamicForm extends VerySimpleModel {
         if (!isset($this->_fields)) {
             $this->_fields = array();
             foreach ($this->getDynamicFields() as $f)
-                $this->_fields[] = $f->getImpl();
+                // TODO: Index by field name or id
+                $this->_fields[$f->get('id')] = $f->getImpl($f);
         }
         return $this->_fields;
     }
@@ -111,22 +112,24 @@ class DynamicForm extends VerySimpleModel {
         }
         return $inst;
     }
-
-    static function addFormTypes($types) {
-        static::$types += $types;
-    }
-
-    static function allTypes() {
-        return static::$types;
-    }
 }
 
 class UserForm extends DynamicForm {
+    static $instance;
+
     static function objects() {
         $os = parent::objects();
         return $os->filter(array('type'=>'U'));
     }
 
+    static function getInstance() {
+        if (!isset(static::$instance)) {
+            $o = static::objects();
+            static::$instance = $o[0]->instanciate();
+        }
+        return static::$instance;
+    }
+
     function getStaticForm() {
         static $form = null;
         if (!$form)
@@ -134,21 +137,45 @@ class UserForm extends DynamicForm {
             'email' => new TextboxField(array(
                 'id'=>'email', 'label'=>'Email Address', 'required'=>true,
                 'validator' => 'email', 'configuration'=>array(
-                    'autocomplete'=>false, 'classes'=>array('typeahead'),
+                    'autocomplete'=>false, 'classes'=>'typeahead',
                     'size'=>40)
             )),
             'name' => new TextboxField(array(
                 'id'=>'name', 'label'=>'Full Name', 'required'=>true,
                 'configuration' => array('size'=>40),
             )),
-            'phone' => new PhoneField(array(
-                'id'=>'c', 'label'=>'Phone Number', 'required'=>false, 'default'=>'',
-                )),
             ), 'User Information');
         return $form;
     }
 }
 
+class TicketForm extends DynamicForm {
+    static $instance;
+
+    static function objects() {
+        $os = parent::objects();
+        return $os->filter(array('type'=>'T'));
+    }
+
+    static function getInstance() {
+        if (!isset(static::$instance)) {
+            $o = static::objects();
+            static::$instance = $o[0]->instanciate();
+        }
+        return static::$instance;
+    }
+}
+// Add fields from the standard ticket form to the ticket filterable fields
+Filter::addSupportedMatches('Dynamic Fields', function() {
+    $matches = array();
+    foreach (TicketForm::getInstance()->getFields() as $f) {
+        if (!$f->hasData())
+            continue;
+        $matches['field.'.$f->get('id')] = $f->getLabel();
+    }
+    return $matches;
+});
+
 require_once(INCLUDE_DIR . "class.json.php");
 
 class DynamicFormField extends VerySimpleModel {
@@ -200,8 +227,8 @@ class DynamicFormField extends VerySimpleModel {
      * errors. If false, the errors were written into the received errors
      * array.
      */
-    function setConfiguration($errors) {
-        $errors = $config = array();
+    function setConfiguration(&$errors=array()) {
+        $config = array();
         foreach ($this->getConfigurationForm() as $name=>$field) {
             $config[$name] = $field->getClean();
             $errors = array_merge($errors, $field->errors());
@@ -212,6 +239,19 @@ class DynamicFormField extends VerySimpleModel {
         return count($errors) === 0;
     }
 
+    function isDeletable() {
+        return $this->get('edit_mask') & 1;
+    }
+    function isNameEditable() {
+        return $this->get('edit_mask') & 2;
+    }
+    function isPrivacyForced() {
+        return $this->get('edit_mask') & 4;
+    }
+    function isRequirementForced() {
+        return $this->get('edit_mask') & 8;
+    }
+
     function delete() {
         // Don't really delete form fields as that will screw up the data
         // model. Instead, just drop the association with the form which
@@ -265,6 +305,8 @@ class DynamicFormEntry extends VerySimpleModel {
     var $_values;
     var $_fields;
     var $_form;
+    var $_errors = false;
+    var $_clean = false;
 
     function getAnswers() {
         if (!isset($this->_values)) {
@@ -283,6 +325,16 @@ class DynamicFormEntry extends VerySimpleModel {
                 return $ans->getValue();
         return null;
     }
+    function setAnswer($name, $value, $id=false) {
+        foreach ($this->getAnswers() as $ans) {
+            if ($ans->getField()->get('name') == $name) {
+                $ans->set('value', $value);
+                if ($id !== false)
+                    $ans->set('value_id', $id);
+                break;
+            }
+        }
+    }
 
     function errors() {
         return $this->_errors;
@@ -292,7 +344,7 @@ class DynamicFormEntry extends VerySimpleModel {
     function getInstructions() { return $this->getForm()->getInstructions(); }
 
     function getForm() {
-        if (!$this->_form) {
+        if (!isset($this->_form)) {
             $this->_form = DynamicForm::lookup($this->get('form_id'));
             if ($this->id)
                 $this->_form->data($this);
@@ -301,7 +353,7 @@ class DynamicFormEntry extends VerySimpleModel {
     }
 
     function getFields() {
-        if (!$this->_fields) {
+        if (!isset($this->_fields)) {
             $this->_fields = array();
             foreach ($this->getAnswers() as $a)
                 $this->_fields[] = $a->getField();
@@ -350,6 +402,10 @@ class DynamicFormEntry extends VerySimpleModel {
         $this->object_id = $user_id;
     }
 
+    function render($staff=true) {
+        return $this->getForm()->render($staff);
+    }
+
     /**
      * addMissingFields
      *
@@ -359,20 +415,26 @@ class DynamicFormEntry extends VerySimpleModel {
      * entry.
      */
     function addMissingFields() {
-        foreach ($this->getForm()->getFields() as $field) {
+        foreach ($this->getForm()->getDynamicFields() as $field) {
             $found = false;
             foreach ($this->getAnswers() as $answer) {
                 if ($answer->get('field_id') == $field->get('id')) {
                     $found = true; break;
                 }
             }
-            if (!$found) {
+            if (!$found && ($field = $field->getImpl($field))
+                    && !$field->isPresentationOnly()) {
                 $a = DynamicFormEntryAnswer::create(
                     array('field_id'=>$field->get('id'), 'entry_id'=>$this->id));
                 $a->field = $field;
+                $a->entry = $this;
                 // Add to list of answers
                 $this->_values[] = $a;
-                $a->save();
+                $this->_fields[] = $field;
+                // Omit fields without data
+                if ($field->hasData())
+                    $a->save();
+                unset($this->_form);
             }
         }
     }
@@ -382,9 +444,18 @@ class DynamicFormEntry extends VerySimpleModel {
             $this->set('updated', new SqlFunction('NOW'));
         parent::save();
         foreach ($this->getAnswers() as $a) {
-            $a->set('value', $a->getField()->to_database($a->getField()->getClean()));
+            $field = $a->getField();
+            $val = $field->to_database($field->getClean());
+            if (is_array($val)) {
+                $a->set('value', $val[0]);
+                $a->set('value_id', $val[1]);
+            }
+            else
+                $a->set('value', $val);
             $a->set('entry_id', $this->get('id'));
-            $a->save();
+            // Don't save answers for presentation-only fields
+            if ($field->hasData() && !$field->isPresentationOnly())
+                $a->save();
         }
         $this->_values = array();
     }
@@ -393,6 +464,7 @@ class DynamicFormEntry extends VerySimpleModel {
         $inst = parent::create($ht);
         $inst->set('created', new SqlFunction('NOW'));
         foreach ($inst->getForm()->getFields() as $f) {
+            if (!$f->hasData()) continue;
             $a = DynamicFormEntryAnswer::create(
                 array('field_id'=>$f->get('id')));
             $a->field = $f;
@@ -440,7 +512,8 @@ class DynamicFormEntryAnswer extends VerySimpleModel {
 
     function getField() {
         if (!isset($this->field)) {
-            $this->field = DynamicFormField::lookup($this->get('field_id'))->getImpl();
+            $f = DynamicFormField::lookup($this->get('field_id'));
+            $this->field = $f->getImpl($f);
             $this->field->answer = $this;
         }
         return $this->field;
@@ -448,10 +521,15 @@ class DynamicFormEntryAnswer extends VerySimpleModel {
 
     function getValue() {
         if (!$this->_value)
-            $this->_value = $this->getField()->to_php($this->get('value'));
+            $this->_value = $this->getField()->to_php(
+                $this->get('value'), $this->get('value_id'));
         return $this->_value;
     }
 
+    function getIdValue() {
+        return $this->get('value_id');
+    }
+
     function toString() {
         return $this->getField()->toString($this->getValue());
     }
@@ -490,7 +568,7 @@ class DynamicList extends VerySimpleModel {
     }
 
     function getPluralName() {
-        if ($name = $this->get('plural_name'))
+        if ($name = $this->get('name_plural'))
             return $name;
         else
             return $this->get('name') . 's';
@@ -530,13 +608,13 @@ class DynamicList extends VerySimpleModel {
         $selections = array();
         foreach (DynamicList::objects() as $list) {
             $selections['list-'.$list->id] =
-                array('Selection: ' .  $list->getPluralName(),
+                array($list->getPluralName(),
                     SelectionField, $list->get('id'));
         }
         return $selections;
     }
 }
-FormField::addFieldTypes(array(DynamicList, 'getSelections'));
+FormField::addFieldTypes('Custom Lists', array(DynamicList, 'getSelections'));
 
 /**
  * Represents a single item in a dynamic list
@@ -576,12 +654,14 @@ class DynamicListItem extends VerySimpleModel {
 }
 
 class SelectionField extends FormField {
+    function getListId() {
+        list(,$list_id) = explode('-', $this->get('type'));
+        return $list_id;
+    }
+
     function getList() {
-        if (!$this->_list) {
-            $list_id = explode('-', $this->get('type'));
-            $list_id = $list_id[1];
-            $this->_list = DynamicList::lookup($list_id);
-        }
+        if (!$this->_list)
+            $this->_list = DynamicList::lookup($this->getListId());
         return $this->_list;
     }
 
@@ -589,33 +669,35 @@ class SelectionField extends FormField {
         return new SelectionWidget($this);
     }
 
-    function parse($id) {
-        return $this->to_php($id);
+    function parse($value) {
+        return $this->to_php($value);
     }
 
-    function to_php($id) {
-        if (!$id)
-            return null;
-        list($id, $value) = explode(':', $id);
-        $item = DynamicListItem::lookup($id);
+    function to_php($value, $id=false) {
+        $item = DynamicListItem::lookup($id ? $id : $value);
         # Attempt item lookup by name too
         if (!$item) {
-            $item = DynamicListItem::objects()->filter(array(
-                        'value'=>$id,
-                        'list_id'=>$this->getList()->get('id')));
-            $item = (count($item)) ? $item[0] : null;
+            $item = DynamicListItem::lookup(array(
+                'value'=>$value,
+                'list_id'=>$this->getListId()));
         }
-        return $item;
+        return ($item) ? $item : $id;
     }
 
     function to_database($item) {
-        if ($item && $item->get('id'))
-            return $item->id . ':' . $item->value;
+        if ($item instanceof DynamicListItem)
+            return array($item->value, $item->id);
         return null;
     }
 
     function toString($item) {
-        return ($item) ? $item->toString() : '';
+        return ($item instanceof DynamicListItem) ? $item->toString() : $item;
+    }
+
+    function validateEntry($item) {
+        parent::validateEntry($item);
+        if ($item && !$item instanceof DynamicListItem)
+            $this->_errors[] = 'Select a value from the list';
     }
 
     function getConfigurationOptions() {
@@ -627,21 +709,30 @@ class SelectionField extends FormField {
                 'hint'=>'Typeahead will work better for large lists')),
         );
     }
+
+    function getChoices() {
+        if (!$this->_choices) {
+            $this->_choices = array();
+            foreach ($this->getList()->getItems() as $i)
+                $this->_choices[$i->get('id')] = $i->get('value');
+        }
+        return $this->_choices;
+    }
 }
 
 class SelectionWidget extends ChoicesWidget {
     function render() {
         $config = $this->field->getConfiguration();
         $value = false;
-        if (is_object($this->value) && get_class($this->value) == 'DynamicListItem') {
+        if ($this->value instanceof DynamicListItem) {
             // Loaded from database
             $value = $this->value->get('id');
             $name = $this->value->get('value');
         } elseif ($this->value) {
             // Loaded from POST
             $value = $this->value;
-            $name = DynamicListItem::lookup($this->value);
-            $name = ($name) ? $name->get('value') : null;
+            $name = DynamicListItem::lookup($value);
+            $name = ($name) ? $name->get('value') : $value;
         }
 
         if (!$config['typeahead']) {
@@ -652,22 +743,20 @@ class SelectionWidget extends ChoicesWidget {
         $source = array();
         foreach ($this->field->getList()->getItems() as $i)
             $source[] = array(
-                'info' => $i->get('value'),
-                'value' => strtolower($i->get('value').' '.$i->get('extra')),
-                'id' => $i->get('id'));
+                'value' => $i->get('value'),
+                'info' => $i->get('value')." -- ".$i->get('extra'),
+            );
         ?>
         <span style="display:inline-block">
-        <input type="hidden" name="<?php echo $this->name; ?>"
-            value="<?php echo $value; ?>" />
-        <input type="text" size="30" id="<?php echo $this->name; ?>"
-            value="<?php echo $name; ?>" />
+        <input type="text" size="30" name="<?php echo $this->name; ?>"
+            value="<?php echo $name; ?>" autocomplete="off" />
         <script type="text/javascript">
         $(function() {
-            $('#<?php echo $this->name; ?>').typeahead({
+            $('input[name=<?php echo $this->name; ?>]').typeahead({
                 source: <?php echo JsonDataEncoder::encode($source); ?>,
+                property: 'info',
                 onselect: function(item) {
-                    $('#<?php echo $this->name; ?>').val(item['info'])
-                    $('input[name="<?php echo $this->name; ?>"]').val(item['id'])
+                    $('input[name="<?php echo $this->name; ?>"]').val(item['value'])
                 }
             });
         });
@@ -675,15 +764,5 @@ class SelectionWidget extends ChoicesWidget {
         </span>
         <?php
     }
-
-    function getChoices() {
-        if (!$this->_choices) {
-            $this->_choices = array();
-            foreach ($this->field->getList()->getItems() as $i)
-                $this->_choices[$i->get('id')] = $i->get('value');
-        }
-        return $this->_choices;
-    }
 }
-
 ?>
diff --git a/include/class.filter.php b/include/class.filter.php
index 1f94120793d338ddb57be5979eb31a500d9c1ada..2ee4fbbf2e1a967b9ab32a0f29d7b7f872a0e962 100644
--- a/include/class.filter.php
+++ b/include/class.filter.php
@@ -18,6 +18,17 @@ class Filter {
     var $id;
     var $ht;
 
+    static $match_types = array(
+        'Basic Fields' => array(
+            'name'      => 'Name',
+            'email'     => 'Email',
+            'subject'   => 'Subject',
+            'body'      => 'Body/Text',
+            'reply-to'  => 'Reply-To Email',
+            'reply-to-name' => 'Reply-To Name',
+        ),
+    );
+
     function Filter($id) {
         $this->id=0;
         $this->load($id);
@@ -299,13 +310,28 @@ class Filter {
             $ticket['cannedResponseId'] = $this->getCannedResponse();
     }
     /* static */ function getSupportedMatches() {
-        return array(
-            'name'=>    'Name',
-            'email'=>   'Email',
-            'subject'=> 'Subject',
-            'body'=>    'Body/Text'
-        );
+        foreach (static::$match_types as $k=>&$v) {
+            if (is_callable($v))
+                $v = $v();
+        }
+        unset($v);
+        return static::$match_types;
+    }
+
+    static function addSupportedMatches($group, $callable) {
+        static::$match_types[$group] = $callable;
     }
+
+    static function getSupportedMatchFields() {
+        $keys = array();
+        foreach (static::getSupportedMatches() as $group=>$matches) {
+            foreach ($matches as $key=>$label) {
+                $keys[] = $key;
+            }
+        }
+        return $keys;
+    }
+
     /* static */ function getSupportedMatchTypes() {
         return array(
             'equal'=>       'Equal',
@@ -370,7 +396,7 @@ class Filter {
 
     function save_rules($id,$vars,&$errors) {
 
-        $matches = array_keys(self::getSupportedMatches());
+        $matches = array_keys(self::getSupportedMatchFields());
         $types = array_keys(self::getSupportedMatchTypes());
 
         $rules=array();
@@ -382,7 +408,9 @@ class Filter {
                     $errors["rule_$i"]='Invalid match type selection';
                 elseif(!$vars["rule_v$i"])
                     $errors["rule_$i"]='Value required';
-                elseif($vars["rule_w$i"]=='email' && $vars["rule_h$i"]=='equal' && !Validator::is_email($vars["rule_v$i"]))
+                elseif($vars["rule_w$i"]=='email'
+                        && $vars["rule_h$i"]=='equal'
+                        && !Validator::is_email($vars["rule_v$i"]))
                     $errors["rule_$i"]='Valid email required for the match type';
                 else //for everything-else...we assume it's valid.
                     $rules[]=array('what'=>$vars["rule_w$i"],
@@ -640,22 +668,18 @@ class TicketFilter {
      *  deal with the data in the incoming ticket (based on $vars) will be considered.
      *  @see ::quickList() for more information.
      */
-    function TicketFilter($origin, $vars=null) {
+    function TicketFilter($origin, $vars=array()) {
 
         //Normalize the target based on ticket's origin.
         $this->target = self::origin2target($origin);
 
         //Extract the vars we care about (fields we filter by!).
-         $this->vars = array_filter(array_map('trim',
-                 array(
-                     'email'     => $vars['email'],
-                     'subject'   => $vars['subject'],
-                     'name'      => $vars['name'],
-                     'body'      => $vars['message'],
-                     'emailId'   => $vars['emailId'],
-                     'reply-to'  => @$vars['reply-to'],
-                     'reply-to-name' => @$vars['reply-to-name'],
-                 )));
+        $this->vars = array('body'=>$vars['message']);
+        $interest = Filter::getSupportedMatchFields();
+        foreach ($vars as $k=>$v) {
+            if (in_array($k, $interest))
+                $this->vars[$k] = trim($v);
+        }
 
          //Init filters.
         $this->build();
diff --git a/include/class.forms.php b/include/class.forms.php
index cc5d07f07821a713fdb9370bc9954dd00662b883..b9a6a2dbe8225171c9e50131ea92135de309895a 100644
--- a/include/class.forms.php
+++ b/include/class.forms.php
@@ -20,7 +20,7 @@
  */
 class Form {
     var $fields = array();
-    var $title = 'Unnamed Form';
+    var $title = '';
     var $instructions = '';
 
     var $_errors;
@@ -93,14 +93,21 @@ class FormField {
     );
 
     var $_cform;
+    var $_clean;
+    var $_errors = array();
+    var $parent;
 
     static $types = array(
-        'text'  => array('Short Answer', TextboxField),
-        'memo' => array('Long Answer', TextareaField),
-        'datetime' => array('Date and Time', DatetimeField),
-        'phone' => array('Phone Number', PhoneField),
-        'bool' => array('Checkbox', BooleanField),
-        'choices' => array('Choices', ChoiceField),
+        'Basic Fields' => array(
+            'text'  => array('Short Answer', TextboxField),
+            'memo' => array('Long Answer', TextareaField),
+            'thread' => array('Thread Entry', ThreadEntryField, false),
+            'datetime' => array('Date and Time', DatetimeField),
+            'phone' => array('Phone Number', PhoneField),
+            'bool' => array('Checkbox', BooleanField),
+            'choices' => array('Choices', ChoiceField),
+            'break' => array('Section Break', SectionBreakField),
+        ),
     );
     static $more_types = array();
 
@@ -114,20 +121,25 @@ class FormField {
             $this->ht['id'] = $uid++;
     }
 
-    static function addFieldTypes($callable) {
-        static::$more_types[] = $callable;
+    static function addFieldTypes($group, $callable) {
+        static::$more_types[$group] = $callable;
     }
 
     static function allTypes() {
         if (static::$more_types) {
-            foreach (static::$more_types as $c)
-                static::$types = array_merge(static::$types,
-                    call_user_func($c));
+            foreach (static::$more_types as $group=>$c)
+                static::$types[$group] = call_user_func($c);
             static::$more_types = array();
         }
         return static::$types;
     }
 
+    static function getFieldType($type) {
+        foreach (static::allTypes() as $group=>$types)
+            if (isset($types[$type]))
+                return $types[$type];
+    }
+
     function get($what) {
         return $this->ht[$what];
     }
@@ -141,15 +153,16 @@ class FormField {
      * user-entered data.
      */
     function getClean() {
-        $value = $this->getWidget()->value;
-        $value = $this->parse($value);
-        $this->validateEntry($value);
-        return $value;
+        if (!isset($this->_clean)) {
+            $value = $this->getWidget()->value;
+            $this->_clean = $this->parse($value);
+            $this->validateEntry($this->_clean);
+        }
+        return $this->_clean;
     }
 
     function errors() {
-        if (!$this->_errors) return array();
-        else return $this->_errors;
+        return $this->_errors;
     }
 
     function isValidEntry() {
@@ -168,14 +181,13 @@ class FormField {
      * $value - (string) input from the user
      */
     function validateEntry($value) {
+        if (!$value && count($this->_errors))
+            return;
+
         # Validates a user-input into an instance of this field on a dynamic
         # form
-        if (!is_array($this->_errors)) {
-            $this->_errors = array();
-
-            if ($this->get('required') && !$value)
-                $this->_errors[] = $this->getLabel() . ' is a required field';
-        }
+        if ($this->get('required') && !$value && $this->hasData())
+            $this->_errors[] = sprintf('%s is a required field', $this->getLabel());
     }
 
     /**
@@ -253,13 +265,20 @@ class FormField {
      * For instance, if the value of this field is 'text', a TextField
      * instance will be returned.
      */
-    function getImpl() {
+    function getImpl($parent=null) {
         // Allow registration with ::addFieldTypes and delayed calling
-        $types = static::allTypes();
-        $clazz = $types[$this->get('type')][1];
+        $this->parent = $parent;
+        $type = static::getFieldType($this->get('type'));
+        $clazz = $type[1];
         return new $clazz($this->ht);
     }
 
+    function __call($what, $args) {
+        // XXX: Throw exception if $this->parent is not set
+        return call_user_func_array(
+            array($this->parent, $what), $args);
+    }
+
     function getAnswer() { return $this->answer; }
 
     function getFormName() {
@@ -269,8 +288,12 @@ class FormField {
             return $this->get('id');
     }
 
-    function render() {
-        $this->getWidget()->render();
+    function render($mode=null) {
+        $this->getWidget()->render($mode);
+    }
+
+    function renderExtras($mode=null) {
+        return;
     }
 
     function getConfigurationOptions() {
@@ -300,14 +323,49 @@ class FormField {
         return $this->_config;
     }
 
+    /**
+     * If the [Config] button should be shown to allow for the configuration
+     * of this field
+     */
     function isConfigurable() {
         return true;
     }
 
+    /**
+     * Field type is changeable in the admin interface
+     */
+    function isChangeable() {
+        return true;
+    }
+
+    /**
+     * Field does not contain data that should be saved to the database. Ie.
+     * non data fields like section headers
+     */
+    function hasData() {
+        return true;
+    }
+
+    /**
+     * Returns true if the field/widget should be rendered as an entire
+     * block in the target form.
+     */
+    function isBlockLevel() {
+        return false;
+    }
+
+    /**
+     * Fields should not be saved with the dynamic data. It is assumed that
+     * some static processing will store the data elsewhere.
+     */
+    function isPresentationOnly() {
+        return false;
+    }
+
     function getConfigurationForm() {
         if (!$this->_cform) {
-            $types = static::allTypes();
-            $clazz = $types[$this->get('type')][1];
+            $type = static::getFieldType($this->get('type'));
+            $clazz = $type[1];
             $T = new $clazz();
             $this->_cform = $T->getConfigurationOptions();
         }
@@ -333,6 +391,10 @@ class TextboxField extends FormField {
                 'id'=>3, 'label'=>'Validator', 'required'=>false, 'default'=>'',
                 'choices' => array('phone'=>'Phone Number','email'=>'Email Address',
                     'ip'=>'IP Address', 'number'=>'Number', ''=>'None'))),
+            'validator-error' => new TextboxField(array(
+                'id'=>4, 'label'=>'Validation Error', 'default'=>'',
+                'configuration'=>array('size'=>40),
+                'hint'=>'Message shown to user if the input does not match the validator')),
         );
     }
 
@@ -354,6 +416,8 @@ class TextboxField extends FormField {
             $config = $this->getConfiguration();
             $valid = $config['validator'];
         }
+        if (!$value || !isset($validators[$valid]))
+            return;
         $func = $validators[$valid];
         if (is_array($func) && is_callable($func[0]))
             if (!call_user_func($func[0], $value))
@@ -442,6 +506,34 @@ class ChoiceField extends FormField {
                 'id'=>1, 'label'=>'Choices', 'required'=>false, 'default'=>'')),
         );
     }
+
+    function toString($value) {
+        $choices = $this->getChoices();
+        if (isset($choices[$value]))
+            return $choices[$value];
+        else
+            return $choices[$this->get('default')];
+    }
+
+    function getChoices() {
+        if ($this->_choices === null) {
+            // Allow choices to be set in this->ht (for configurationOptions)
+            $this->_choices = $this->get('choices');
+            if (!$this->_choices) {
+                $this->_choices = array();
+                $config = $this->getConfiguration();
+                $choices = explode("\n", $config['choices']);
+                foreach ($choices as $choice) {
+                    // Allow choices to be key: value
+                    list($key, $val) = explode(':', $choice);
+                    if ($val == null)
+                        $val = $key;
+                    $this->_choices[trim($key)] = trim($val);
+                }
+            }
+        }
+        return $this->_choices;
+     }
 }
 
 class DatetimeField extends FormField {
@@ -516,6 +608,93 @@ class DatetimeField extends FormField {
     }
 }
 
+/**
+ * This is kind-of a special field that doesn't have any data. It's used as
+ * a field to provide a horizontal section break in the display of a form
+ */
+class SectionBreakField extends FormField {
+    function getWidget() {
+        return new SectionBreakWidget($this);
+    }
+
+    function hasData() {
+        return false;
+    }
+
+    function isBlockLevel() {
+        return true;
+    }
+}
+
+class ThreadEntryField extends FormField {
+    function getWidget() {
+        return new ThreadEntryWidget($this);
+    }
+    function isChangeable() {
+        return false;
+    }
+    function isBlockLevel() {
+        return true;
+    }
+    function isPresentationOnly() {
+        return true;
+    }
+    function renderExtras($mode=null) {
+        if ($mode == 'client')
+            $this->getWidget()->showAttachments();
+    }
+}
+
+class PriorityField extends ChoiceField {
+    function getWidget() {
+        $widget = parent::getWidget();
+        if ($widget->value instanceof Priority)
+            $widget->value = $widget->value->getId();
+        return $widget;
+    }
+
+    function getChoices() {
+        $this->ht['default'] = 0;
+
+        $sql = 'SELECT priority_id, priority_desc FROM '.PRIORITY_TABLE
+              .' ORDER BY priority_urgency DESC';
+        $choices = array(0 => '&mdash; Default &mdash;');
+        if (!($res = db_query($sql)))
+            return $choices;
+
+        while ($row = db_fetch_row($res))
+            $choices[$row[0]] = $row[1];
+        return $choices;
+    }
+
+    function parse($id) {
+        return $this->to_php(null, $id);
+    }
+
+    function to_php($value, $id) {
+        return Priority::lookup($id);
+    }
+
+    function to_database($prio) {
+        return ($prio instanceof Priority)
+            ? array($prio->getDesc(), $prio->getId())
+            : $prio;
+    }
+
+    function toString($value) {
+        return ($value instanceof Priority) ? $value->getDesc() : $value;
+    }
+
+    function getConfigurationOptions() {
+        return array();
+    }
+}
+FormField::addFieldTypes('Built-in Lists', function() {
+    return array(
+        'priority' => array('Priority Level', PriorityField),
+    );
+});
+
 class Widget {
     function Widget() {
         # Not called in PHP5
@@ -546,7 +725,7 @@ class TextboxWidget extends Widget {
         if (isset($config['length']))
             $maxlength = "maxlength=\"{$config['length']}\"";
         if (isset($config['classes']))
-            $classes = 'class="'.implode(' ', $config['classes']).'"';
+            $classes = 'class="'.$config['classes'].'"';
         if (isset($config['autocomplete']))
             $autocomplete = 'autocomplete="'.($config['autocomplete']?'on':'off').'"';
         ?>
@@ -604,7 +783,7 @@ class ChoicesWidget extends Widget {
         // Determine the value for the default (the one listed if nothing is
         // selected)
         $def_key = $this->field->get('default');
-        $choices = $this->getChoices();
+        $choices = $this->field->getChoices();
         $have_def = isset($choices[$def_key]);
         if (!$have_def)
             $def_val = 'Select '.$this->field->get('label');
@@ -619,34 +798,14 @@ class ChoicesWidget extends Widget {
             foreach ($choices as $key=>$name) {
                 if (!$have_def && $key == $def_key)
                     continue; ?>
-                <option value="<?php echo $key; ?>"
-                <?php if ($this->value == $key) echo 'selected="selected"';
+                <option value="<?php echo $key; ?>" <?php
+                    if ($this->value == $key) echo 'selected="selected"';
                 ?>><?php echo $name; ?></option>
             <?php } ?>
         </select>
         </span>
         <?php
     }
-
-    function getChoices() {
-        if ($this->_choices === null) {
-            // Allow choices to be set in this->ht (for configurationOptions)
-            $this->_choices = $this->field->get('choices');
-            if (!$this->_choices) {
-                $this->_choices = array();
-                $config = $this->field->getConfiguration();
-                $choices = explode("\n", $config['choices']);
-                foreach ($choices as $choice) {
-                    // Allow choices to be key: value
-                    list($key, $val) = explode(':', $choice);
-                    if ($val == null)
-                        $val = $key;
-                    $this->_choices[trim($key)] = trim($val);
-                }
-            }
-        }
-        return $this->_choices;
-     }
 }
 
 class CheckboxWidget extends Widget {
@@ -730,4 +889,42 @@ class DatetimePickerWidget extends Widget {
     }
 }
 
+class SectionBreakWidget extends Widget {
+    function render() {
+        ?><div class="form-header section-break"><h3><?php
+        echo Format::htmlchars($this->field->get('label'));
+        ?></h3><em><?php echo Format::htmlchars($this->field->get('hint'));
+        ?></em></div>
+        <?php
+    }
+}
+
+class ThreadEntryWidget extends Widget {
+    function render($mode=null) {
+        echo '<strong>'.Format::htmlchars($this->field->get('label'));
+        ?></strong>:
+        <br/>
+        <textarea name="<?php echo $this->field->get('name'); ?>"
+            cols="21" rows="8" style="width:80%;"><?php echo
+            $this->value; ?></textarea>
+    <?php
+    }
+
+    function showAttachments() {
+        global $cfg, $thisclient;
+
+        if(($cfg->allowOnlineAttachments()
+            && !$cfg->allowAttachmentsOnlogin())
+            || ($cfg->allowAttachmentsOnlogin()
+                && ($thisclient && $thisclient->isValid()))) { ?>
+        <hr/>
+        <div><strong>Attachments:</strong></div>
+        <div class="uploads"></div><br>
+        <input type="file" class="multifile" name="attachments[]" id="attachments" size="30" value="" />
+        <font class="error">&nbsp;<?php echo $errors['attachments']; ?></font>
+        <?php
+        }
+    }
+}
+
 ?>
diff --git a/include/class.orm.php b/include/class.orm.php
index 86d3015f1525dff2dd90edea0448309901ac6fbf..50aae3b237f454ec97d26928ed6bbdf7a1ae179b 100644
--- a/include/class.orm.php
+++ b/include/class.orm.php
@@ -151,8 +151,6 @@ class VerySimpleModel {
 
     function save($refetch=false) {
         $pk = static::$meta['pk'];
-        if (!$this->isValid())
-            return false;
         if (!is_array($pk)) $pk=array($pk);
         if ($this->__new__)
             $sql = 'INSERT INTO '.static::$meta['table'];
@@ -632,7 +630,9 @@ class SqlCompiler {
                 list($field, $op) = $this->getField($field, $model);
                 // Allow operators to be callable rather than sprintf
                 // strings
-                if (is_callable($op))
+                if ($value === null)
+                    $filter[] = sprintf('%s IS NULL', $field);
+                elseif (is_callable($op))
                     $filter[] = call_user_func($op, $field, $value);
                 else
                     $filter[] = sprintf($op, $field, $this->input($value));
diff --git a/include/class.ticket.php b/include/class.ticket.php
index 12c30c02acad45938333a2e7de30ef0453cca320..ef82b7104e9f6fdf5a90ef64f34739f6a203b403 100644
--- a/include/class.ticket.php
+++ b/include/class.ticket.php
@@ -111,7 +111,7 @@ class Ticket {
         foreach (DynamicFormEntry::forTicket($this->getId()) as $form)
             foreach ($form->getAnswers() as $answer)
                 $this->_answers[$answer->getField()->get('name')] =
-                    $answer->toString();
+                    $answer->getValue();
     }
 
     function reload() {
@@ -216,7 +216,7 @@ class Ticket {
 
     function getName(){
         if ($o = $this->getOwner())
-            return $o->getFullName();
+            return $o->getName();
         return null;
     }
 
@@ -288,11 +288,17 @@ class Ticket {
     }
 
     function getPriorityId() {
-        return $this->ht['priority_id'];
+        global $cfg;
+
+        if ($a = $this->_answers['priority'])
+            return $a->getId();
+        return $cfg->getDefaultPriorityId();
     }
 
-    function getPriority() { //TODO: Make it an obj.
-        return  $this->ht['priority_desc'];
+    function getPriority() {
+        if ($a = $this->_answers['priority'])
+            return $a->getDesc();
+        return '...ummm...';
     }
 
     function getPhone() {
@@ -565,20 +571,6 @@ class Ticket {
         return $this->lastMsgId=$msgid;
     }
 
-    function setPriority($priorityId) {
-
-        //XXX: what happens to SLA priority???
-
-        if(!$priorityId || $priorityId==$this->getPriorityId())
-            return ($priorityId);
-
-        $sql='UPDATE '.TICKET_TABLE.' SET updated=NOW() '
-            .', priority_id='.db_input($priorityId)
-            .' WHERE ticket_id='.db_input($this->getId());
-
-        return (($res=db_query($sql)) && db_affected_rows($res));
-    }
-
     //DeptId can NOT be 0. No orphans please!
     function setDeptId($deptId) {
 
@@ -1664,7 +1656,6 @@ class Ticket {
 
         $fields=array();
         $fields['topicId']  = array('type'=>'int',      'required'=>1, 'error'=>'Help topic required');
-        $fields['priorityId'] = array('type'=>'int',    'required'=>1, 'error'=>'Priority required');
         $fields['slaId']    = array('type'=>'int',      'required'=>0, 'error'=>'Select SLA');
         $fields['duedate']  = array('type'=>'date',     'required'=>0, 'error'=>'Invalid date - must be MM/DD/YY');
 
@@ -1687,7 +1678,6 @@ class Ticket {
         if($errors) return false;
 
         $sql='UPDATE '.TICKET_TABLE.' SET updated=NOW() '
-            .' ,priority_id='.db_input($vars['priorityId'])
             .' ,topic_id='.db_input($vars['topicId'])
             .' ,sla_id='.db_input($vars['slaId'])
             .' ,duedate='.($vars['duedate']?db_input(date('Y-m-d G:i',Misc::dbtime($vars['duedate'].' '.$vars['time']))):'NULL');
@@ -1895,6 +1885,23 @@ class Ticket {
             }
         }
 
+        // Create and verify the dynamic form entry for the new ticket
+        $form = TicketForm::getInstance();
+        // If submitting via email, ensure we have a subject and such
+        foreach ($form->getFields() as $field) {
+            $fname = $field->get('name');
+            if ($fname && isset($vars[$fname]) && !$field->value)
+                $field->value = $vars[$fname];
+        }
+
+        // Don't enforce form validation for email
+        if (!$form->isValid() && strtolower($origin) != 'email')
+            $errors += $form->errors();
+
+        // Unpack dynamic variables into $vars for filter application
+        foreach ($form->getFields() as $f)
+            $vars['field.'.$f->get('id')] = $f->toString($f->getClean());
+
         //Init ticket filters...
         $ticket_filter = new TicketFilter($origin, $vars);
         // Make sure email contents should not be rejected
@@ -1930,7 +1937,6 @@ class Ticket {
                 # TODO: Return error message
                 $errors['err']=$errors['origin'] = 'Invalid origin given';
         }
-        $fields['priorityId']   = array('type'=>'int',      'required'=>0, 'error'=>'Invalid Priority');
 
         if(!Validator::process($fields, $vars, $errors) && !$errors['err'])
             $errors['err'] ='Missing or invalid data - check the errors and try again';
@@ -1945,16 +1951,19 @@ class Ticket {
                 $errors['duedate']='Due date must be in the future';
         }
 
-        //Any error above is fatal.
-        if($errors)  return 0;
-
         # Identify the user creating the ticket
         $user_info = UserForm::getStaticForm()->getClean();
+        // Data is slightly different between HTTP posts and emails
         if (isset($vars['emailId']) || !isset($user_info['email']))
             $user_info = $vars;
+        elseif (!UserForm::getStaticForm()->isValid())
+            $errors['user'] = 'Incomplete client information';
         $user = User::fromForm($user_info);
         $user_email = UserEmail::ensure($user_info['email']);
 
+        //Any error above is fatal.
+        if($errors)  return 0;
+
         # Perform ticket filter actions on the new ticket arguments
         if ($ticket_filter) $ticket_filter->apply($vars);
 
@@ -1964,13 +1973,13 @@ class Ticket {
 
         // OK...just do it.
         $deptId=$vars['deptId']; //pre-selected Dept if any.
-        $priorityId=$vars['priorityId'];
         $source=ucfirst($vars['source']);
         $topic=NULL;
         // Intenal mapping magic...see if we need to override anything
         if(isset($vars['topicId']) && ($topic=Topic::lookup($vars['topicId']))) { //Ticket created via web by user/or staff
             $deptId=$deptId?$deptId:$topic->getDeptId();
-            $priorityId=$priorityId?$priorityId:$topic->getPriorityId();
+            if (!$form->getAnswer('priority'))
+                $form->setAnswer('priority', null, $topic->getPriorityId());
             if($autorespond) $autorespond=$topic->autoRespond();
             $source=$vars['source']?$vars['source']:'Web';
 
@@ -1988,13 +1997,15 @@ class Ticket {
 
         }elseif($vars['emailId'] && !$vars['deptId'] && ($email=Email::lookup($vars['emailId']))) { //Emailed Tickets
             $deptId=$email->getDeptId();
-            $priorityId=$priorityId?$priorityId:$email->getPriorityId();
+            if (!$form->getAnswer('priority'))
+                $form->setAnswer('priority', null, $email->getPriorityId());
             if($autorespond) $autorespond=$email->autoRespond();
             $email=null;
             $source='Email';
         }
         //Last minute checks
-        $priorityId=$priorityId?$priorityId:$cfg->getDefaultPriorityId();
+        if (!$form->getAnswer('priority'))
+            $form->setAnswer('priority', null, $cfg->getDefaultPriorityId());
         $deptId=$deptId?$deptId:$cfg->getDefaultDeptId();
         $topicId=$vars['topicId']?$vars['topicId']:0;
         $ipaddress=$vars['ip']?$vars['ip']:$_SERVER['REMOTE_ADDR'];
@@ -2008,7 +2019,6 @@ class Ticket {
             .' ,ticketID='.db_input($extId)
             .' ,dept_id='.db_input($deptId)
             .' ,topic_id='.db_input($topicId)
-            .' ,priority_id='.db_input($priorityId)
             .' ,ip_address='.db_input($ipaddress)
             .' ,source='.db_input($source);
 
@@ -2029,6 +2039,10 @@ class Ticket {
             //TODO: RETHING what happens if this fails?? [At the moment on failure random ID is used...making stuff usable]
         }
 
+        // Save the (common) dynamic form
+        $form->setTicketId($id);
+        $form->save();
+
         $dept = $ticket->getDept();
 
         //post the message.
@@ -2100,11 +2114,6 @@ class Ticket {
         if($vars['source'] && !in_array(strtolower($vars['source']),array('email','phone','other')))
             $errors['source']='Invalid source - '.Format::htmlchars($vars['source']);
 
-        if(!$vars['issue'])
-            $errors['issue']='Summary of the issue required';
-        else
-            $vars['message']=$vars['issue'];
-
         if(!($ticket=Ticket::create($vars, $errors, 'staff', false, (!$vars['assignId']))))
             return false;
 
diff --git a/include/class.topic.php b/include/class.topic.php
index d924f2ab7f1f4e3afec14e4cde5f20ebb069705a..8f1eb7365d1a323363c34744d12f0156648b11f7 100644
--- a/include/class.topic.php
+++ b/include/class.topic.php
@@ -209,15 +209,6 @@ class Topic {
         elseif(($tid=self::getIdByName($vars['topic'], $vars['pid'])) && $tid!=$id)
             $errors['topic']='Topic already exists';
 
-        if (!$vars['form_id'])
-            $errors['form_id'] = 'You must select a form';
-        else {
-            $form=DynamicForm::lookup($vars['form_id']);
-            foreach (array('subject') as $f)
-                if (!$form->hasField($f))
-                    $errors['form_id']="Form must define the '$f' field";
-        }
-
         if(!$vars['dept_id'])
             $errors['dept_id']='You must select a department';
 
diff --git a/include/client/open.inc.php b/include/client/open.inc.php
index ac8a9353d4c585acdd610d65d6d22f8c9d533d04..7bb1d959951f2946ab421bf2954e30ff18db5482 100644
--- a/include/client/open.inc.php
+++ b/include/client/open.inc.php
@@ -39,7 +39,9 @@ $info=($_POST && $errors)?Format::htmlchars($_POST):$info;
             <font class="error">*&nbsp;<?php echo $errors['topicId']; ?></font>
         </td>
     </tr>
-    <?php UserForm::getStaticForm()->render(false, 'Your Information'); ?>
+<?php
+        UserForm::getStaticForm()->render(false, 'Your Information');
+        TicketForm::getInstance()->render(false); ?>
     </tbody>
     <tbody id="dynamic-form">
         <?php if ($forms) {
@@ -49,32 +51,6 @@ $info=($_POST && $errors)?Format::htmlchars($_POST):$info;
         } ?>
     </tbody>
     <tbody>
-    <tr><td colspan="2"><hr /></td></tr>
-    <tr>
-        <td class="required">Message:</td>
-        <td>
-            <div style="margin-bottom:0.5em;"><em>Please provide as much detail as possible so we can best assist you.</em> <font class="error">*&nbsp;<?php echo $errors['message']; ?></font>
-                </div>
-            <textarea id="message" cols="60" rows="8" name="message"
-                class="richtext ifhtml draft"
-                data-draft-namespace="ticket.client"
-                data-draft-object-id="<?php echo substr(session_id(), -12); ?>"
-                ><?php echo $info['message']; ?></textarea>
-        </td>
-    </tr>
-
-    <?php if(($cfg->allowOnlineAttachments() && !$cfg->allowAttachmentsOnlogin())
-            || ($cfg->allowAttachmentsOnlogin() && ($thisclient && $thisclient->isValid()))) { ?>
-    <tr>
-        <td>Attachments:</td>
-        <td>
-            <div class="uploads"></div><br>
-            <input type="file" class="multifile" name="attachments[]" id="attachments" size="30" value="" />
-            <font class="error">&nbsp;<?php echo $errors['attachments']; ?></font>
-        </td>
-    </tr>
-    <tr><td colspan=2>&nbsp;</td></tr>
-    <?php } ?>
     <?php
     if($cfg->allowPriorityChange() && ($priorities=Priority::getPriorities())) { ?>
     <tr>
diff --git a/include/client/templates/dynamic-form.tmpl.php b/include/client/templates/dynamic-form.tmpl.php
index 49ff0169731bbb1d1c4e15f512dd698bcb04e1ef..1e4e79e8c2c486c5ab32a39cf9ab2c81dfaf6efd 100644
--- a/include/client/templates/dynamic-form.tmpl.php
+++ b/include/client/templates/dynamic-form.tmpl.php
@@ -30,14 +30,22 @@
         if ($field->get('private'))
             continue;
         ?>
-        <tr><td class="<?php if ($field->get('required')) echo 'required'; ?>">
-            <?php echo Format::htmlchars($field->get('label')); ?>:</td>
-            <td><?php $field->render(); ?>
+        <tr>
+            <?php if ($field->isBlockLevel()) { ?>
+                <td colspan="2">
+            <?php
+            }
+            else { ?>
+                <td class="<?php if ($field->get('required')) echo 'required'; ?>">
+                <?php echo Format::htmlchars($field->get('label')); ?>:</td><td>
+            <?php
+            }
+            $field->render('client'); ?>
             <?php if ($field->get('required')) { ?>
                 <font class="error">*</font>
             <?php
             }
-            if ($field->get('hint')) { ?>
+            if ($field->get('hint') && !$field->isBlockLevel()) { ?>
                 <br /><em style="color:gray;display:inline-block"><?php
                     echo Format::htmlchars($field->get('hint')); ?></em>
             <?php
@@ -45,7 +53,9 @@
             foreach ($field->errors() as $e) { ?>
                 <br />
                 <font class="error"><?php echo $e; ?></font>
-            <?php } ?>
+            <?php }
+            $field->renderExtras('client');
+            ?>
             </td>
         </tr>
         <?php
diff --git a/include/client/tickets.inc.php b/include/client/tickets.inc.php
index 04898d5e43e5a27e02907087079e118bb572251a..d38d113cedc3fb6e744640ffd224090a0e01b970 100644
--- a/include/client/tickets.inc.php
+++ b/include/client/tickets.inc.php
@@ -46,14 +46,14 @@ $qselect='SELECT ticket.ticket_id,ticket.ticketID,ticket.dept_id,isanswered, '
 $dynfields='(SELECT entry.object_id, value FROM '.FORM_ANSWER_TABLE.' ans '.
          'LEFT JOIN '.FORM_ENTRY_TABLE.' entry ON entry.id=ans.entry_id '.
          'LEFT JOIN '.FORM_FIELD_TABLE.' field ON field.id=ans.field_id '.
-         'WHERE field.name = "%1$s" entry.object_type="T") %1$s ON ticket.ticket_id = %1$s.ticket_id ';
+         'WHERE field.name = "%1$s" AND entry.object_type="T")';
 $subject_sql = sprintf($dynfields, 'subject');
 
 $qfrom='FROM '.TICKET_TABLE.' ticket '
       .' LEFT JOIN '.DEPT_TABLE.' dept ON (ticket.dept_id=dept.dept_id) '
       .' LEFT JOIN '.USER_TABLE.' user ON user.id = ticket.user_id'
       .' LEFT JOIN '.USER_EMAIL_TABLE.' email ON user.id = email.user_id'
-      .' LEFT JOIN '.$subject_sql;
+      .' LEFT JOIN '.$subject_sql.' subject ON ticket.ticket_id = subject.object_id ';
 
 $qwhere =' WHERE email.address='.db_input($thisclient->getEmail());
 
diff --git a/include/i18n/en_US/form.yaml b/include/i18n/en_US/form.yaml
index 868b76422ce8d669cb96ce74150338311e291f23..1de219b60033189782e9bd1d3dcaae1a85a4a066 100644
--- a/include/i18n/en_US/form.yaml
+++ b/include/i18n/en_US/form.yaml
@@ -1,44 +1,83 @@
 #
-# Default (dynamic) form configuration
+# Default (dynamic) form configuration. This data is used as the initial,
+# minimal data for dynamic forms that ships with the system.
 #
 # Fields:
+# id:       Used only if associated with a help topic
+# title:    Bold section title of the form
+# instructions: Title deck, detailed instructions on entering form data
+# notes:    Notes for the form, shown under the fields
+# deletable: True if the form can be removed from the system
+# fields:   List of fields for the form
+#   type:       Field type (short name) (eg. 'text', 'memo', 'phone', ...)
+#   label:      Field label shown to the user
+#   name:       Name used with the data internally. This is especially
+#               useful for page and email templates, where %{ ticket.<name> }
+#               will be used to retrieve the data from the field.
+#   hint:       Help text shown with the field
+#   edit_mask:  Mask out edits to the field (1=>delete, 2=>change name,
+#                   4=>privacy setting, 8=>requirement setting)
+#   private:    True if the field should be hidden from the client
+#   required:   True if entry for the field is required
+#   configuration: Field-specific configuration
+#     size:     (text) width of the field
+#     length:   (text) maximum size of the data in the field
+#     cols:     (memo) width of the textarea
+#     rows:     (memo) height of the textarea
+#
 ---
 - id: 1
-  title: User Information
+  type: U # notrans
+  title: Client Details
+  deletable: false
   fields:
-    - type: text # notrans
-      name: email # notrans
-      label: Email Address
-      required: true
-      sort: 10
-      configuration:
-          size: 40
-          length: 40
-          validator: email # notrans
-
-    - type: text # notrans
-      name: name # notrans
-      label: Full Name
-      required: true
-      sort: 20
-      configuration:
-          size: 40
-          length: 40
-
     - type: phone # notrans
       name: phone # notrans
       label: Phone Number
       required: false
-      sort: 30
+      sort: 1
+
+    - type: memo # notrans
+      name: notes
+      label: Internal Notes
+      required: false
+      private: true
+      sort: 2
+      configuration:
+        rows: 4
+        cols: 40
 
 - id: 2
-  title: Ticket Details
+  type: T # notrans
+  title: Standard Ticket Form
+  notes: |
+      This form will be attached to every ticket, regardless of its source.
+      You can add any fields to this form and they will be available to all
+      tickets, and will be searchable with advanced search and filterable.
+  deletable: false
   fields:
     - type: text # notrans
       name: subject # notrans
       label: Subject
       hint: Issue Summary
-      sort: 10
+      required: true
+      edit_mask: 15
+      sort: 1
       configuration:
-          size: 40
-          length: 50
+        size: 40
+        length: 50
+
+    - type: thread # notrans
+      name: message # notrans
+      label: Issue
+      hint: Details on the reason(s) for opening the ticket.
+      required: true
+      edit_mask: 15
+      sort: 2
+
+    - type: priority # notrans
+      name: priority # notrans
+      label: Priority Level
+      required: false
+      edit_mask: 3
+      sort: 3
diff --git a/include/staff/dynamic-form.inc.php b/include/staff/dynamic-form.inc.php
index 4af7cde38f102f5d5ee9e2ad57305b1817c11ed6..34ba48ffdf9be605772bddbd8c1fb4b92e26d388 100644
--- a/include/staff/dynamic-form.inc.php
+++ b/include/staff/dynamic-form.inc.php
@@ -16,7 +16,7 @@ if($form && $_REQUEST['a']!='add') {
 $info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
 
 ?>
-<form action="?" method="post" id="save">
+<form action="?id=<?php echo urlencode($_REQUEST['id']); ?>" method="post" id="save">
     <?php csrf_token(); ?>
     <input type="hidden" name="do" value="<?php echo $action; ?>">
     <input type="hidden" name="id" value="<?php echo $info['id']; ?>">
@@ -43,12 +43,6 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
                 echo $info['instructions']; ?></textarea>
             </td>
         </tr>
-        <tr>
-            <td width="180">Internal Notes:</td>
-            <td><textarea name="notes" rows="4" cols="80"><?php
-                echo $info['notes']; ?></textarea>
-            </td>
-        </tr>
     </tbody>
     </table>
     <table class="form_table" width="940" border="0" cellspacing="0" cellpadding="2">
@@ -78,11 +72,22 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
             <td>Email Address</td><td>Short Answer</td><td>email</td>
             <td><input type="checkbox" disabled="disabled"/></td>
             <td><input type="checkbox" disabled="disabled" checked="checked"/></td></tr>
+    <?php
+        $uform = UserForm::objects()->all();
+        $ftypes = FormField::allTypes();
+        foreach ($uform[0]->getFields() as $f) {
+            if ($f->get('private')) continue;
+        ?>
         <tr>
             <td><input type="checkbox" disabled="disabled"/></td>
-            <td>Phone Number</td><td>Phone Number</td><td>phone</td>
+            <td><?php echo $f->get('label'); ?></td>
+            <td><?php $t=FormField::getFieldType($f->get('type')); echo $t[0]; ?></td>
+            <td><?php echo $f->get('name'); ?></td>
             <td><input type="checkbox" disabled="disabled"/></td>
-            <td><input type="checkbox" disabled="disabled"/></td></tr>
+            <td><input type="checkbox" disabled="disabled"
+                <?php echo $f->get('required') ? 'checked="checked"' : ''; ?>/></td></tr>
+
+        <?php } ?>
     </tbody>
     <thead>
         <tr>
@@ -102,9 +107,14 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
     <tbody class="sortable-rows" data-sort="sort-">
     <?php if ($form) foreach ($form->getFields() as $f) {
         $id = $f->get('id');
+        $deletable = ($f->get('editable') & 1) ? 'disabled="disabled"' : '';
+        $force_name = ($f->get('editable') & 2) ? 'disabled="disabled"' : '';
+        $force_privacy = ($f->get('editable') & 4) ? 'disabled="disabled"' : '';
+        $force_required = ($f->get('editable') & 8) ? 'disabled="disabled"' : '';
         $errors = $f->errors(); ?>
         <tr>
-            <td><input type="checkbox" name="delete-<?php echo $id; ?>"/>
+            <td><input type="checkbox" name="delete-<?php echo $id; ?>"
+                    <?php echo $deletable; ?>/>
                 <input type="hidden" name="sort-<?php echo $id; ?>"
                     value="<?php echo $f->get('sort'); ?>"/>
                 <font class="error"><?php
@@ -113,11 +123,18 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
                 </td>
             <td><input type="text" size="32" name="label-<?php echo $id; ?>"
                 value="<?php echo $f->get('label'); ?>"/></td>
-            <td><select name="type-<?php echo $id; ?>">
-                <?php foreach (FormField::allTypes() as $type=>$nfo) { ?>
+            <td><select name="type-<?php echo $id; ?>" <?php
+                if (!$f->isChangeable()) echo 'disabled="disabled"'; ?>>
+                <?php foreach (FormField::allTypes() as $group=>$types) {
+                        ?><optgroup label="<?php echo Format::htmlchars($group); ?>"><?php
+                        foreach ($types as $type=>$nfo) {
+                            if ($f->get('type') != $type
+                                    && isset($nfo[2]) && !$nfo[2]) continue; ?>
                 <option value="<?php echo $type; ?>" <?php
                     if ($f->get('type') == $type) echo 'selected="selected"'; ?>>
                     <?php echo $nfo[0]; ?></option>
+                    <?php } ?>
+                </optgroup>
                 <?php } ?>
             </select>
             <?php if ($f->isConfigurable()) { ?>
@@ -133,15 +150,24 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
             <?php } ?></td>
             <td>
                 <input type="text" size="20" name="name-<?php echo $id; ?>"
-                    value="<?php echo $f->get('name'); ?>"/>
+                    value="<?php echo $f->get('name'); ?>" <?php echo $force_name ?>/>
                 <font class="error"><?php
                     if ($errors['name']) echo '<br/>'; echo $errors['name'];
                 ?></font>
                 </td>
             <td><input type="checkbox" name="private-<?php echo $id; ?>"
-                <?php if ($f->get('private')) echo 'checked="checked"'; ?>/></td>
+                <?php if ($f->get('private')) echo 'checked="checked"'; ?>
+                <?php echo $force_privacy ?>/></td>
             <td><input type="checkbox" name="required-<?php echo $id; ?>"
-                <?php if ($f->get('required')) echo 'checked="checked"'; ?>/></td>
+                <?php if ($f->get('required')) echo 'checked="checked"'; ?>
+                <?php echo $force_required ?>/>
+            <?php if (!$f->get('editable')) { ?>
+                <input type="hidden" name="private-<?php echo $id; ?>" value="<?php
+                    echo ($f->get('private')) ? 'on' : ''; ?>" />
+                <input type="hidden" name="required-<?php echo $id; ?>" value="<?php
+                    echo ($f->get('required')) ? 'on' : ''; ?>" />
+            <?php
+            } ?></td>
         </tr>
     <?php
     }
@@ -150,17 +176,35 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
                 <input type="hidden" name="sort-new-<?php echo $i; ?>"/></td>
             <td><input type="text" size="32" name="label-new-<?php echo $i; ?>"/></td>
             <td><select name="type-new-<?php echo $i; ?>">
-                <?php foreach (FormField::allTypes() as $type=>$nfo) { ?>
+                <?php foreach (FormField::allTypes() as $group=>$types) {
+                    ?><optgroup label="<?php echo Format::htmlchars($group); ?>"><?php
+                    foreach ($types as $type=>$nfo) { ?>
                 <option value="<?php echo $type; ?>">
                     <?php echo $nfo[0]; ?></option>
+                    <?php } ?>
+                </optgroup>
                 <?php } ?>
             </select></td>
             <td><input type="text" size="20" name="name-new-<?php echo $i; ?>"/></td>
-            <td><input type="checkbox" name="private-new-<?php echo $i; ?>"/></td>
+            <td><input type="checkbox" name="private-new-<?php echo $i; ?>"
+                <?php if ($form && $form->get('type') == 'U')
+                    echo 'checked="checked"'; ?>/></td>
             <td><input type="checkbox" name="required-new-<?php echo $i; ?>"/></td>
         </tr>
     <?php } ?>
     </tbody>
+    <tbody>
+        <tr>
+            <th colspan="7">
+                <em><strong>Internal Notes:</strong> be liberal, they're internal</em>
+            </th>
+        </tr>
+        <tr>
+            <td colspan="7"><textarea name="notes" rows="6" cols="80" style="width:95%"><?php
+                echo $info['notes']; ?></textarea>
+            </td>
+        </tr>
+    </tbody>
     </table>
 <p style="padding-left:225px;">
     <input type="submit" name="submit" value="<?php echo $submit_text; ?>">
diff --git a/include/staff/dynamic-forms.inc.php b/include/staff/dynamic-forms.inc.php
index e1c590831ecc218c805feede51946006b30677ad..7c590d435d522c37eddaccfff32e14abb99f22a7 100644
--- a/include/staff/dynamic-forms.inc.php
+++ b/include/staff/dynamic-forms.inc.php
@@ -7,13 +7,50 @@
 
 <?php
 $page = ($_GET['p'] && is_numeric($_GET['p'])) ? $_GET['p'] : 1;
-$count = DynamicForm::objects()->count();
+$count = DynamicForm::objects()->filter(array('type'=>'G'))->count();
 $pageNav = new Pagenate($count, $page, PAGE_LIMIT);
 $pageNav->setURL('forms.php');
 $showing=$pageNav->showing().' forms';
 ?>
 
 <table class="list" border="0" cellspacing="1" cellpadding="0" width="940">
+    <thead>
+        <tr>
+            <th width="7">&nbsp;</th>
+            <th>Client Information Form <em>Added to all clients</em></th>
+            <th>Last Updated</th>
+        </tr>
+    </thead>
+    <tbody>
+    <?php foreach (UserForm::objects()->order_by('title')
+                ->limit($pageNav->getLimit())
+                ->offset($pageNav->getStart()) as $form) { ?>
+        <tr>
+            <td/>
+            <td><a href="?id=<?php echo $form->get('id'); ?>"><?php echo $form->get('title'); ?></a></td>
+            <td><?php echo $form->get('updated'); ?></td>
+        </tr>
+    <?php } ?>
+    </tbody>
+    <thead>
+        <tr>
+            <th width="7">&nbsp;</th>
+            <th>Common Ticket Form <em>Added to all tickets</em></th>
+            <th>Last Updated</th>
+        </tr>
+    </thead>
+    <tbody>
+    <?php foreach (TicketForm::objects()->order_by('title')
+                ->limit($pageNav->getLimit())
+                ->offset($pageNav->getStart()) as $form) { ?>
+        <tr>
+            <td/>
+            <td><a href="?id=<?php echo $form->get('id'); ?>"><?php echo $form->get('title'); ?></a></td>
+            <td><?php echo $form->get('updated'); ?></td>
+        </tr>
+    <?php } ?>
+    </tbody>
+    <tbody>
     <caption><?php echo $showing; ?></caption>
     <thead>
         <tr>
@@ -23,7 +60,8 @@ $showing=$pageNav->showing().' forms';
         </tr>
     </thead>
     <tbody>
-    <?php foreach (DynamicForm::objects()->order_by('title')
+    <?php foreach (DynamicForm::objects()->filter(array('type'=>'G'))
+                ->order_by('title')
                 ->limit($pageNav->getLimit())
                 ->offset($pageNav->getStart()) as $form) { ?>
         <tr>
diff --git a/include/staff/filter.inc.php b/include/staff/filter.inc.php
index b029fbb682a8ebfdebf386027d561c772aba2382..37ac6c69925bf4dc15711f0a76767803aeacc66a 100644
--- a/include/staff/filter.inc.php
+++ b/include/staff/filter.inc.php
@@ -125,11 +125,14 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
                     <select name="rule_w<?php echo $i; ?>">
                         <option value="">&mdash; Select One &dash;</option>
                         <?php
-                        foreach($matches as $k=>$v){
-                            $sel=($info["rule_w$i"]==$k)?'selected="selected"':'';
-                            echo sprintf('<option value="%s" %s>%s</option>',$k,$sel,$v);
-                        }
-                        ?>
+                        foreach ($matches as $group=>$ms) { ?>
+                            <optgroup label="<?php echo $group; ?>"><?php
+                            foreach ($ms as $k=>$v) {
+                                $sel=($info["rule_w$i"]==$k)?'selected="selected"':'';
+                                echo sprintf('<option value="%s" %s>%s</option>',$k,$sel,$v);
+                            } ?>
+                        </optgroup>
+                        <?php } ?>
                     </select>
                     <select name="rule_h<?php echo $i; ?>">
                         <option value="0">&mdash; Select One &dash;</option>
diff --git a/include/staff/helptopic.inc.php b/include/staff/helptopic.inc.php
index 8aa9be725e396a07c0da9cf07b58a38c46fe8e12..319ce28509d32c1118180f3e1cf359b87b2c00eb 100644
--- a/include/staff/helptopic.inc.php
+++ b/include/staff/helptopic.inc.php
@@ -92,8 +92,8 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
        <tr>
            <td><strong>Dynamic Form</strong>:</td>
            <td><select name="form_id">
-               <option value="0">&mdash; Select a Form &mdash;</option>
-               <?php foreach (DynamicForm::objects() as $group) { ?>
+               <option value="0">&mdash; No Extra Fields &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"'; ?>>
@@ -101,7 +101,7 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
                    </option>
                <?php } ?>
                </select>
-               <em>Information for tickets associated with this help topic</em>
+               <em>Extra information for tickets associated with this help topic</em>
                &nbsp;<span class="error">&nbsp;<?php echo $errors['form_id']; ?></span>
            </td>
        </tr>
diff --git a/include/staff/templates/dynamic-form.tmpl.php b/include/staff/templates/dynamic-form.tmpl.php
index e54d5bfc1395a52fff47f07649aa5b0a3c1646cf..4a67b38e2a94a664e86d2702e442dbdd56c9704c 100644
--- a/include/staff/templates/dynamic-form.tmpl.php
+++ b/include/staff/templates/dynamic-form.tmpl.php
@@ -7,14 +7,21 @@
     }
     foreach ($form->getFields() as $field) {
         ?>
-        <tr><td class="multi-line <?php if ($field->get('required')) echo 'required'; ?>">
-            <?php echo Format::htmlchars($field->get('label')); ?>:</td>
-            <td><?php $field->render(); ?>
+        <tr><?php if ($field->isBlockLevel()) { ?>
+                <td colspan="2" class="<?php if ($field->get('required')) echo 'required'; ?>">
+                <?php
+            }
+            else { ?>
+                <td class="multi-line <?php if ($field->get('required')) echo 'required'; ?>">
+                <?php echo Format::htmlchars($field->get('label')); ?>:</td>
+                <td><?php
+            }
+            $field->render(); ?>
             <?php if ($field->get('required')) { ?>
                 <font class="error">*</font>
             <?php
             }
-            if ($field->get('hint')) { ?>
+            if ($field->get('hint') && !$field->isBlockLevel()) { ?>
                 <br /><em style="color:gray;display:inline-block"><?php
                     echo Format::htmlchars($field->get('hint')); ?></em>
             <?php
diff --git a/include/staff/ticket-edit.inc.php b/include/staff/ticket-edit.inc.php
index 643e147c0bb6e535846038f1a81f99460053afd7..e2d7864a42c710fd50c049d15e655cacfd00c9cf 100644
--- a/include/staff/ticket-edit.inc.php
+++ b/include/staff/ticket-edit.inc.php
@@ -51,25 +51,6 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$ticket->getUpdateInfo());
                 &nbsp;<font class="error"><b>*</b>&nbsp;<?php echo $errors['topicId']; ?></font>
             </td>
         </tr>
-        <tr>
-            <td width="160" class="required">
-                Priority Level:
-            </td>
-            <td>
-                <select name="priorityId">
-                    <option value="" selected >&mdash; Select Priority &mdash;</option>
-                    <?php
-                    if($priorities=Priority::getPriorities()) {
-                        foreach($priorities as $id =>$name) {
-                            echo sprintf('<option value="%d" %s>%s</option>',
-                                    $id, ($info['priorityId']==$id)?'selected="selected"':'',$name);
-                        }
-                    }
-                    ?>
-                </select>
-                &nbsp;<font class="error">*&nbsp;<?php echo $errors['priorityId']; ?></font>
-            </td>
-        </tr>
         <tr>
             <td width="160">
                 SLA Plan:
@@ -111,7 +92,7 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$ticket->getUpdateInfo());
         <tbody id="dynamic-form">
         <?php if ($forms)
             foreach ($forms as $form) {
-                include(STAFFINC_DIR . 'templates/dynamic-form.tmpl.php');
+                $form->render(true);
         } ?>
         </tbody>
         <tbody>
diff --git a/include/staff/ticket-open.inc.php b/include/staff/ticket-open.inc.php
index e72514b938c083373a151c45559b05dd4721884f..46356bbba95148e310f473abe0f21a259698c45b 100644
--- a/include/staff/ticket-open.inc.php
+++ b/include/staff/ticket-open.inc.php
@@ -88,25 +88,6 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
                 &nbsp;<font class="error"><b>*</b>&nbsp;<?php echo $errors['topicId']; ?></font>
             </td>
         </tr>
-        <tr>
-            <td width="160">
-                Priority:
-            </td>
-            <td>
-                <select name="priorityId">
-                    <option value="0" selected >&mdash; System Default &mdash;</option>
-                    <?php
-                    if($priorities=Priority::getPriorities()) {
-                        foreach($priorities as $id =>$name) {
-                            echo sprintf('<option value="%d" %s>%s</option>',
-                                    $id, ($info['priorityId']==$id)?'selected="selected"':'',$name);
-                        }
-                    }
-                    ?>
-                </select>
-                &nbsp;<font class="error">&nbsp;<?php echo $errors['priorityId']; ?></font>
-            </td>
-         </tr>
          <tr>
             <td width="160">
                 SLA Plan:
@@ -178,29 +159,16 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
             </td>
         </tr>
         <?php
-        } ?>
+        }
+        TicketForm::getInstance()->render(true);
+        ?>
         </tbody>
         <tbody id="dynamic-form">
-        <?php if ($form)
-            include(STAFFINC_DIR . 'templates/dynamic-form.tmpl.php');
+        <?php
+            if ($form) $form->render(true);
         ?>
         </tbody>
         <tbody>
-        <tr>
-            <th colspan="2">
-                <em><strong>Issue</strong>: The user will be able to see the issue summary below and any associated responses.</em>
-            </th>
-        </tr>
-        <tr>
-            <td colspan=2>
-                <div><em><strong>Issue</strong>: Details on the reason(s) for opening the ticket.</em> <font class="error">*&nbsp;<?php echo $errors['issue']; ?></font></div>
-                <textarea class="richtext ifhtml draft draft-delete"
-                    placeholder="Details on the reason(s) for opening the ticket."
-                    data-draft-namespace="ticket.staff" name="issue"
-                    cols="21" rows="8" style="width:80%;"
-                    ><?php echo $info['issue']; ?></textarea>
-            </td>
-        </tr>
         <?php
         //is the user allowed to post replies??
         if($thisstaff->canPostReply()) {
diff --git a/include/staff/ticket-view.inc.php b/include/staff/ticket-view.inc.php
index e99560eb6e8b2c2ecb8f789ecf3a2acb2dad51fc..a3ef79d5d4d29f0752ef720c41cd769ab318d6df 100644
--- a/include/staff/ticket-view.inc.php
+++ b/include/staff/ticket-view.inc.php
@@ -140,7 +140,7 @@ if($ticket->isOverdue())
                 </tr>
             </table>
         </td>
-        <td width="50%">
+        <td width="50%" style="vertical-align:top">
             <table border="0" cellspacing="" cellpadding="4" width="100%">
                 <tr>
                     <th width="100">Client:</th>
@@ -177,15 +177,11 @@ if($ticket->isOverdue())
                     </td>
                 </tr>
                 <tr>
-                    <th>Default Email:</th>
+                    <th>Email:</th>
                     <td>
                     <?php echo $ticket->getEmail(); ?>
                     </td>
                 </tr>
-                <tr>
-                    <th>Phone:</th>
-                    <td><?php echo $ticket->getPhoneNumber(); ?></td>
-                </tr>
                 <tr>
                     <th>Source:</th>
                     <td><?php
@@ -193,8 +189,6 @@ if($ticket->isOverdue())
 
                         if($ticket->getIP())
                             echo '&nbsp;&nbsp; <span class="faded">('.$ticket->getIP().')</span>';
-
-
                         ?>
                     </td>
                 </tr>
@@ -285,7 +279,7 @@ foreach (DynamicFormEntry::forTicket($ticket->getId()) as $form) {
     //           array('email', ...))));
     $answers = array_filter($form->getAnswers(), function ($a) {
         return !in_array($a->getField()->get('name'),
-                array('email','subject','name','phone'));
+                array('email','subject','name','priority'));
         });
     if (count($answers) == 0)
         continue;
@@ -293,7 +287,8 @@ foreach (DynamicFormEntry::forTicket($ticket->getId()) as $form) {
         </tr><tr>
         <td colspan="2">
             <table cellspacing="0" cellpadding="4" width="100%" border="0">
-            <?php foreach($answers as $a) { ?>
+            <?php foreach($answers as $a) {
+                if (!$a->toString()) continue; ?>
                 <tr>
                     <th width="100"><?php
     echo $a->getField()->get('label');
diff --git a/include/staff/tickets.inc.php b/include/staff/tickets.inc.php
index 5d1ea62383fb4cf523b6723e88da82a4afc15d99..43e68f52567066a78290809100ee96418a4c27d8 100644
--- a/include/staff/tickets.inc.php
+++ b/include/staff/tickets.inc.php
@@ -49,7 +49,7 @@ switch(strtolower($_REQUEST['status'])){ //Status is overloaded
         $results_type='Answered Tickets';
         break;
     default:
-        if(!$search)
+        if(!$search && !isset($_REQUEST['advsid']))
             $_REQUEST['status']=$status='open';
 }
 
@@ -109,90 +109,36 @@ if($search):
     if($searchTerm){
         $qstr.='&query='.urlencode($searchTerm);
         $queryterm=db_real_escape($searchTerm,false); //escape the term ONLY...no quotes.
-        if(is_numeric($searchTerm)){
+        if (is_numeric($searchTerm)) {
             $qwhere.=" AND ticket.ticketID LIKE '$queryterm%'";
-        }elseif(strpos($searchTerm,'@') && Validator::is_email($searchTerm)){ //pulling all tricks!
+        } elseif (strpos($searchTerm,'@') && Validator::is_email($searchTerm)) {
+            //pulling all tricks!
             # XXX: What about searching for email addresses in the body of
             #      the thread message
             $qwhere.=" AND email.address='$queryterm'";
-        }else{//Deep search!
+        } else {//Deep search!
             //This sucks..mass scan! search anything that moves!
+            require_once(INCLUDE_DIR.'ajax.tickets.php');
 
-            $deep_search=true;
-        }
-    }
-    // OwnerId
-    if ($_REQUEST['ownerId']) {
-        $qwhere .= ' AND ticket.user_id='.db_input($_REQUEST['ownerId']);
-    }
-    //department
-    if($_REQUEST['deptId'] && in_array($_REQUEST['deptId'],$thisstaff->getDepts())) {
-        //This is dept based search..perm taken care above..put the sucker in.
-        $qwhere.=' AND ticket.dept_id='.db_input($_REQUEST['deptId']);
-        $qstr.='&deptId='.urlencode($_REQUEST['deptId']);
-    }
-
-    //Help topic
-    if($_REQUEST['topicId']) {
-        $qwhere.=' AND ticket.topic_id='.db_input($_REQUEST['topicId']);
-        $qstr.='&topicId='.urlencode($_REQUEST['topicId']);
-    }
-
-    //Assignee
-    if(isset($_REQUEST['assignee']) && strcasecmp($_REQUEST['status'], 'closed'))  {
-        $id=preg_replace("/[^0-9]/", "", $_REQUEST['assignee']);
-        $assignee = $_REQUEST['assignee'];
-        $qstr.='&assignee='.urlencode($_REQUEST['assignee']);
-        $qwhere.= ' AND (
-                ( ticket.status="open" ';
-
-        if($assignee[0]=='t')
-            $qwhere.='  AND ticket.team_id='.db_input($id);
-        elseif($assignee[0]=='s')
-            $qwhere.='  AND ticket.staff_id='.db_input($id);
-        elseif(is_numeric($id))
-            $qwhere.='  AND ticket.staff_id='.db_input($id);
-
-       $qwhere.=' ) ';
-
-        if($_REQUEST['staffId'] && !$_REQUEST['status']) { //Assigned TO + Closed By
-            $qwhere.= ' OR (ticket.staff_id='.db_input($_REQUEST['staffId']). ' AND ticket.status="closed") ';
-            $qstr.='&staffId='.urlencode($_REQUEST['staffId']);
-        }elseif(isset($_REQUEST['staffId'])) {
-            $qwhere.= ' OR ticket.status="closed" ';
-            $qstr.='&staffId='.urlencode($_REQUEST['staffId']);
-        }
-
-        $qwhere.= ' ) ';
-    } elseif($_REQUEST['staffId']) {
-        $qwhere.=' AND (ticket.staff_id='.db_input($_REQUEST['staffId']).' AND ticket.status="closed") ';
-        $qstr.='&staffId='.urlencode($_REQUEST['staffId']);
-    }
-
-    //dates
-    $startTime  =($_REQUEST['startDate'] && (strlen($_REQUEST['startDate'])>=8))?strtotime($_REQUEST['startDate']):0;
-    $endTime    =($_REQUEST['endDate'] && (strlen($_REQUEST['endDate'])>=8))?strtotime($_REQUEST['endDate']):0;
-    if( ($startTime && $startTime>time()) or ($startTime>$endTime && $endTime>0)){
-        $errors['err']='Entered date span is invalid. Selection ignored.';
-        $startTime=$endTime=0;
-    }else{
-        //Have fun with dates.
-        if($startTime){
-            $qwhere.=' AND ticket.created>=FROM_UNIXTIME('.$startTime.')';
-            $qstr.='&startDate='.urlencode($_REQUEST['startDate']);
-
-        }
-        if($endTime){
-            $qwhere.=' AND ticket.created<=FROM_UNIXTIME('.$endTime.')';
-            $qstr.='&endDate='.urlencode($_REQUEST['endDate']);
+            $tickets = TicketsAjaxApi::_search(array('query'=>$queryterm));
+            if (count($tickets))
+                $qwhere .= ' AND ticket.ticket_id IN ('.
+                    implode(',',db_input($tickets)).')';
         }
    }
 
 endif;
 
-$sortOptions=array('date'=>'ticket.created','ID'=>'ticketID','pri'=>'priority_urgency','name'=>'name.value',
-                   'subj'=>'subject.value','status'=>'ticket.status','assignee'=>'assigned','staff'=>'staff',
-                   'dept'=>'dept_name');
+if ($_REQUEST['advsid'] && isset($_SESSION['adv_'.$_REQUEST['advsid']])) {
+    $qstr.='advsid='.$_REQUEST['advsid'];
+    $qwhere .= ' AND ticket.ticket_id IN ('. implode(',',
+        db_input($_SESSION['adv_'.$_REQUEST['advsid']])).')';
+}
+
+$sortOptions=array('date'=>'ticket.created','ID'=>'ticketID',
+    'pri'=>'priority_urgency','name'=>'user.name','subj'=>'subject.value',
+    'status'=>'ticket.status','assignee'=>'assigned','staff'=>'staff',
+    'dept'=>'dept_name');
 
 $orderWays=array('DESC'=>'DESC','ASC'=>'ASC');
 
@@ -241,11 +187,12 @@ $$x=' class="'.strtolower($order).'" ';
 if($_GET['limit'])
     $qstr.='&limit='.urlencode($_GET['limit']);
 
-$dynfields='(SELECT entry.object_id, value FROM '.FORM_ANSWER_TABLE.' ans '.
+$dynfields='(SELECT entry.object_id, value, value_id FROM '.FORM_ANSWER_TABLE.' ans '.
          'LEFT JOIN '.FORM_ENTRY_TABLE.' entry ON entry.id=ans.entry_id '.
          'LEFT JOIN '.FORM_FIELD_TABLE.' field ON field.id=ans.field_id '.
          'WHERE field.name = "%1$s" AND entry.object_type="T")';
 $subject_sql=sprintf($dynfields, 'subject');
+$prio_sql=sprintf($dynfields, 'priority');
 
 $qselect ='SELECT DISTINCT ticket.ticket_id,lock_id,ticketID,ticket.dept_id,ticket.staff_id,ticket.team_id '
     .' ,subject.value as subject'
@@ -257,7 +204,8 @@ $qfrom=' FROM '.TICKET_TABLE.' ticket '.
        ' LEFT JOIN '.USER_TABLE.' user ON user.id = ticket.user_id'.
        ' LEFT JOIN '.USER_EMAIL_TABLE.' email ON user.id = email.user_id'.
        ' LEFT JOIN '.DEPT_TABLE.' dept ON ticket.dept_id=dept.dept_id '.
-       ' LEFT JOIN '.$subject_sql.' subject ON subject.object_id = ticket.ticket_id';
+       ' LEFT JOIN '.$subject_sql.' subject ON subject.object_id = ticket.ticket_id'.
+       ' LEFT JOIN '.$prio_sql.' tprio ON tprio.object_id = ticket.ticket_id';
 
 $sjoin='';
 if($search && $deep_search) {
@@ -282,7 +230,7 @@ $qselect.=' ,count(attach.attach_id) as attachments '
          .' ,IF(staff.staff_id IS NULL,team.name,CONCAT_WS(" ", staff.lastname, staff.firstname)) as assigned '
          .' ,IF(ptopic.topic_pid IS NULL, topic.topic, CONCAT_WS(" / ", ptopic.topic, topic.topic)) as helptopic ';
 
-$qfrom.=' LEFT JOIN '.TICKET_PRIORITY_TABLE.' pri ON (ticket.priority_id=pri.priority_id) '
+$qfrom.=' LEFT JOIN '.TICKET_PRIORITY_TABLE.' pri ON (tprio.value_id=pri.priority_id) '
        .' LEFT JOIN '.TICKET_LOCK_TABLE.' tlock ON (ticket.ticket_id=tlock.ticket_id AND tlock.expire>NOW()
                AND tlock.staff_id!='.db_input($thisstaff->getId()).') '
        .' LEFT JOIN '.TICKET_ATTACHMENT_TABLE.' attach ON (ticket.ticket_id=attach.ticket_id) '
@@ -644,6 +592,17 @@ $negorder=$order=='DESC'?'ASC':'DESC'; //Negate the sorting..
             <span>TO</span>
             <input class="dp" type="input" size="20" name="endDate">
         </fieldset>
+        <fieldset>
+        <?php
+        foreach (TicketForm::getInstance()->getFields() as $f) {
+            if (in_array($f->get('type'), array('text', 'memo', 'phone', 'thread')))
+                continue;
+            elseif (!$f->hasData())
+                continue;
+            ?><label><?php echo $f->getLabel(); ?>:</label>
+                <div style="display:inline-block;width: 12.5em;"><?php $f->render(); ?></div>
+        <?php } ?>
+        </fieldset>
         <p>
             <span class="buttons">
                 <input type="submit" value="Search">
diff --git a/include/upgrader/streams/core/d51f303a-DYNAMICF.patch.sql b/include/upgrader/streams/core/d51f303a-DYNAMICF.patch.sql
index 940b9a12b3d260e5ab8c5925f11254be49484726..5fa04072673d4cd67d9d26bfc8c4f412f47cc597 100644
--- a/include/upgrader/streams/core/d51f303a-DYNAMICF.patch.sql
+++ b/include/upgrader/streams/core/d51f303a-DYNAMICF.patch.sql
@@ -10,48 +10,28 @@
  * fields are dropped from the ticket table.
  */
 
-DROP TABLE IF EXISTS `%TABLE_PREFIX%dynamic_formset`;
-CREATE TABLE `%TABLE_PREFIX%dynamic_formset` (
-    `id` int(11) unsigned auto_increment,
-    `title` varchar(255) NOT NULL,
-    `instructions` varchar(512),
-    `notes` text,
-    `created` datetime NOT NULL,
-    `updated` datetime NOT NULL,
-    PRIMARY KEY (`id`)
-) ENGINE=MyISAM  DEFAULT CHARSET=utf8;
-
-DROP TABLE IF EXISTS `%TABLE_PREFIX%dynamic_formset_sections`;
-CREATE TABLE `%TABLE_PREFIX%dynamic_formset_sections` (
-    `id` int(11) unsigned NOT NULL auto_increment,
-    `formset_id` int(11) NOT NULL,
-    `section_id` int(11) NOT NULL,
-    `title` varchar(255),
-    `instructions` text,
-    -- Allow more than one form, sorted in this order
-    `sort` int(11) NOT NULL DEFAULT 1,
-    PRIMARY KEY (`id`)
-) ENGINE=MyISAM  DEFAULT CHARSET=utf8;
-
-DROP TABLE IF EXISTS `%TABLE_PREFIX%dynamic_form_section`;
-CREATE TABLE `%TABLE_PREFIX%dynamic_form` (
+DROP TABLE IF EXISTS `%TABLE_PREFIX%form`;
+CREATE TABLE `%TABLE_PREFIX%form` (
     `id` int(11) unsigned NOT NULL auto_increment,
+    `type` char(1) NOT NULL DEFAULT 'G',
+    `deletable` tinyint(1) NOT NULL DEFAULT 1,
     `title` varchar(255) NOT NULL,
     `instructions` varchar(512),
     `notes` text,
     `created` datetime NOT NULL,
     `updated` datetime NOT NULL,
     PRIMARY KEY (`id`)
-) ENGINE=MyISAM  DEFAULT CHARSET=utf8;
+) DEFAULT CHARSET=utf8;
 
-DROP TABLE IF EXISTS `%TABLE_PREFIX%dynamic_form_field`;
-CREATE TABLE `%TABLE_PREFIX%dynamic_form_field` (
+DROP TABLE IF EXISTS `%TABLE_PREFIX%form_field`;
+CREATE TABLE `%TABLE_PREFIX%form_field` (
     `id` int(11) unsigned NOT NULL auto_increment,
-    `section_id` int(11) unsigned NOT NULL,
+    `form_id` int(11) unsigned NOT NULL,
     `type` varchar(255) NOT NULL DEFAULT 'text',
     `label` varchar(255) NOT NULL,
     `required` tinyint(1) NOT NULL DEFAULT 0,
     `private` tinyint(1) NOT NULL DEFAULT 0,
+    `edit_mask` tinyint(1) NOT NULL DEFAULT 1,
     `name` varchar(64) NOT NULL,
     `configuration` text,
     `sort` int(11) unsigned NOT NULL,
@@ -59,65 +39,33 @@ CREATE TABLE `%TABLE_PREFIX%dynamic_form_field` (
     `created` datetime NOT NULL,
     `updated` datetime NOT NULL,
     PRIMARY KEY (`id`)
-) ENGINE=MyISAM  DEFAULT CHARSET=utf8;
-
--- Create a default form to mimic the previous default form of osTicket < 1.7.1
-INSERT INTO `%TABLE_PREFIX%dynamic_form_section` SET
-    `id` = 1, `title` = 'User Information', `created` = NOW(),
-    `updated` = NOW();
-INSERT INTO `%TABLE_PREFIX%dynamic_form_section` SET
-    `id` = 2, `title` = 'Ticket Details', `created` = NOW(),
-    `updated` = NOW();
-
-INSERT INTO `%TABLE_PREFIX%dynamic_formset` SET
-    `id` = 1, `title` = 'Default', `created` = NOW(), `updated` = NOW();
-
-INSERT INTO `%TABLE_PREFIX%dynamic_formset_sections` SET
-    `formset_id` = 1, `section_id` = 1, `sort` = 10;
-INSERT INTO `%TABLE_PREFIX%dynamic_formset_sections` SET
-    `formset_id` = 1, `section_id` = 2, `sort` = 20;
-
-INSERT INTO `%TABLE_PREFIX%dynamic_form_field` SET
-    `section_id` = 1, `type` = 'text', `label` = 'Email Address',
-    `required` = 1, `configuration` = '{"size":40,"length":120,"validator":"email"}',
-    `name` = 'email', `sort` = 10, `created` = NOW(), `updated` = NOW();
-INSERT INTO `%TABLE_PREFIX%dynamic_form_field` SET
-    `section_id` = 1, `type` = 'text', `label` = 'Full Name',
-    `required` = 1, `configuration` = '{"size":40,"length":32}',
-    `name` = 'name', `sort` = 20, `created` = NOW(), `updated` = NOW();
-INSERT INTO `%TABLE_PREFIX%dynamic_form_field` SET
-    `section_id` = 1, `type` = 'phone', `label` = 'Phone Number',
-    `name` = 'phone', `sort` = 30, `created` = NOW(), `updated` = NOW();
-
-INSERT INTO `%TABLE_PREFIX%dynamic_form_field` SET
-    `section_id` = 2, `type` = 'text', `label` = 'Subject',
-    `hint` = 'Issue summary', `required` = 1,
-    `configuration` = '{"size":40,"length":64}',
-    `name` = 'subject', `sort` = 10, `created` = NOW(), `updated` = NOW();
-
-DROP TABLE IF EXISTS `%TABLE_PREFIX%dynamic_form_entry`;
-CREATE TABLE `%TABLE_PREFIX%dynamic_form_entry` (
+) DEFAULT CHARSET=utf8;
+
+DROP TABLE IF EXISTS `%TABLE_PREFIX%form_entry`;
+CREATE TABLE `%TABLE_PREFIX%form_entry` (
     `id` int(11) unsigned NOT NULL auto_increment,
-    `section_id` int(11) unsigned NOT NULL,
-    `ticket_id` int(11) unsigned,
+    `form_id` int(11) unsigned NOT NULL,
+    `object_id` int(11) unsigned,
+    `object_type` char(1) NOT NULL DEFAULT 'T',
     `sort` int(11) unsigned NOT NULL DEFAULT 1,
     `created` datetime NOT NULL,
     `updated` datetime NOT NULL,
     PRIMARY KEY (`id`),
     KEY `ticket_dyn_form_lookup` (`ticket_id`)
-) ENGINE=MyISAM  DEFAULT CHARSET=utf8;
+) DEFAULT CHARSET=utf8;
 
-DROP TABLE IF EXISTS `%TABLE_PREFIX%dynamic_form_entry_values`;
-CREATE TABLE `%TABLE_PREFIX%dynamic_form_entry_values` (
-    -- references dynamic_form_entry.id
+DROP TABLE IF EXISTS `%TABLE_PREFIX%form_entry_values`;
+CREATE TABLE `%TABLE_PREFIX%form_entry_values` (
+    -- references form_entry.id
     `entry_id` int(11) unsigned NOT NULL,
     `field_id` int(11) unsigned NOT NULL,
     `value` text,
+    `value_id` int(11),
     PRIMARY KEY (`entry_id`, `field_id`)
-) ENGINE=MyISAM  DEFAULT CHARSET=utf8;
+) DEFAULT CHARSET=utf8;
 
-DROP TABLE IF EXISTS `%TABLE_PREFIX%dynamic_list`;
-CREATE TABLE `%TABLE_PREFIX%dynamic_list` (
+DROP TABLE IF EXISTS `%TABLE_PREFIX%list`;
+CREATE TABLE `%TABLE_PREFIX%list` (
     `id` int(11) unsigned NOT NULL auto_increment,
     `name` varchar(255) NOT NULL,
     `name_plural` varchar(255),
@@ -126,10 +74,10 @@ CREATE TABLE `%TABLE_PREFIX%dynamic_list` (
     `created` datetime NOT NULL,
     `updated` datetime NOT NULL,
     PRIMARY KEY (`id`)
-) ENGINE=MyISAM  DEFAULT CHARSET=utf8;
+) DEFAULT CHARSET=utf8;
 
-DROP TABLE IF EXISTS `%TABLE_PREFIX%dynamic_list_items`;
-CREATE TABLE `%TABLE_PREFIX%dynamic_list_items` (
+DROP TABLE IF EXISTS `%TABLE_PREFIX%list_items`;
+CREATE TABLE `%TABLE_PREFIX%list_items` (
     `id` int(11) unsigned NOT NULL auto_increment,
     `list_id` int(11),
     `value` varchar(255) NOT NULL,
@@ -137,68 +85,94 @@ CREATE TABLE `%TABLE_PREFIX%dynamic_list_items` (
     `extra` varchar(255),
     `sort` int(11) NOT NULL DEFAULT 1,
     PRIMARY KEY (`id`),
-    KEY `dynamic_list_item_lookup` (`list_id`)
-) ENGINE=MyISAM  DEFAULT CHARSET=utf8;
+    KEY `list_item_lookup` (`list_id`)
+) DEFAULT CHARSET=utf8;
+
+DROP TABLE IF EXISTS `%TABLE_PREFIX%user`;
+CREATE TABLE `%TABLE_PREFIX%user` (
+  `id` int(10) unsigned NOT NULL auto_increment,
+  `default_email_id` int(10) NOT NULL,
+  `name` varchar(128) NOT NULL,
+  `created` datetime NOT NULL,
+  `updated` datetime NOT NULL,
+  PRIMARY KEY  (`id`)
+);
+
+DROP TABLE IF EXISTS `%TABLE_PREFIX%user_email`;
+CREATE TABLE `%TABLE_PREFIX%user_email` (
+  `id` int(10) unsigned NOT NULL auto_increment,
+  `user_id` int(10) unsigned NOT NULL,
+  `address` varchar(128) NOT NULL,
+  PRIMARY KEY  (`id`),
+  UNIQUE KEY `address` (`address`)
+);
+
+ALTER TABLE `%TABLE_PREFIX%filter_rule`
+    CHANGE `what` `what` varchar(32) NOT NULL;
 
 ALTER TABLE `%TABLE_PREFIX%help_topic`
   ADD `formset_id` int(11) unsigned NOT NULL default '0' AFTER `sla_id`;
 
--- All help topics will link to the default formset
-UPDATE `%TABLE_PREFIX%help_topic` SET `formset_id` = 1;
 
 -- Port data from the ticket table
 -- 1. Create form entries for each ticket
-INSERT INTO `%TABLE_PREFIX%dynamic_form_entry` (
+INSERT INTO `%TABLE_PREFIX%form_entry` (
     `section_id`, `ticket_id`, `sort`, `created`, `updated`)
     SELECT 1, `ticket_id`, 10, `created`, `updated`
     FROM `%TABLE_PREFIX%ticket`;
 
-INSERT INTO `%TABLE_PREFIX%dynamic_form_entry` (
-    `section_id`, `ticket_id`, `sort`, `created`, `updated`)
-    SELECT 2, `ticket_id`, 20, `created`, `updated`
-    FROM `%TABLE_PREFIX%ticket`;
-
--- 2. Copy Name, Email, and Phone from the ticket table to section #1
-INSERT INTO `%TABLE_PREFIX%dynamic_form_entry_values` (
-    `field_id`, `entry_id`, `value`)
-    SELECT A3.`field_id`, A2.`id`, A1.`name`
-    FROM `%TABLE_PREFIX%ticket` A1
-        INNER JOIN `%TABLE_PREFIX%dynamic_form_entry` A2 ON (A1.`ticket_id`
-                = A2.`ticket_id` AND A2.`section_id` = 1),
-        INNER JOIN `%TABLE_PREFIX%dynamic_form_field` A3 ON (A2.`section_id`
-                = A3.`section_id`)
-    WHERE A3.`name` = 'name' AND LENGTH(A1.`name`);
-
-INSERT INTO `%TABLE_PREFIX%dynamic_form_entry_values` (
+-- 2. Copy subject lines from the ticket table into section #2
+INSERT INTO `%TABLE_PREFIX%form_entry_values` (
     `field_id`, `entry_id`, `value`)
-    SELECT A3.`field_id`, A2.`id`, A1.`email`
+    SELECT A3.`field_id`, A2.`id`, A1.`subject`
     FROM `%TABLE_PREFIX%ticket` A1
-        INNER JOIN `%TABLE_PREFIX%dynamic_form_entry` A2 ON (A1.`ticket_id`
-                = A2.`ticket_id` AND A2.`section_id` = 1),
-        INNER JOIN `%TABLE_PREFIX%dynamic_form_field` A3 ON (A2.`section_id`
+        INNER JOIN `%TABLE_PREFIX%form_entry` A2 ON (A1.`ticket_id`
+                = A2.`ticket_id` AND A2.`section_id` = 2),
+        INNER JOIN `%TABLE_PREFIX%form_field` A3 ON (A2.`section_id`
                 = A3.`section_id`)
-    WHERE A3.`name` = 'email' AND LENGTH(A1.`email`);
+    WHERE A3.`name` = 'subject';
 
-INSERT INTO `%TABLE_PREFIX%dynamic_form_entry_values` (
+-- TODO: Move this to a client info dynamic entry
+-- 3. Copy Phone from the ticket table to section #1
+INSERT INTO `%TABLE_PREFIX%form_entry_values` (
     `field_id`, `entry_id`, `value`)
     SELECT A3.`field_id`, A2.`id`, CONCAT(A1.`phone`, 'X', A1.`phone_ext`)
     FROM `%TABLE_PREFIX%ticket` A1
-        INNER JOIN `%TABLE_PREFIX%dynamic_form_entry` A2 ON (A1.`ticket_id`
+        INNER JOIN `%TABLE_PREFIX%form_entry` A2 ON (A1.`ticket_id`
                 = A2.`ticket_id` AND A2.`section_id` = 1),
-        INNER JOIN `%TABLE_PREFIX%dynamic_form_field` A3 ON (A2.`section_id`
+        INNER JOIN `%TABLE_PREFIX%form_field` A3 ON (A2.`section_id`
                 = A3.`section_id`)
     WHERE A3.`name` = 'phone' AND LENGTH(A1.`phone`);
 
--- 3. Copy subject lines from the ticket table into section #2
-INSERT INTO `%TABLE_PREFIX%dynamic_form_entry_values` (
-    `field_id`, `entry_id`, `value`)
-    SELECT A3.`field_id`, A2.`id`, A1.`subject`
+-- 4. Create <user> accounts for everybody
+--      - Start with creating email addresses for the accounts
+INSERT INTO `%TABLE_PREFIX%user_email` (`address`)
+    SELECT DISTINCT `email` FROM `%TABLE_PREFIX%ticket`;
+
+--      - Then create the accounts and link the `default_email`s
+INSERT INTO `%TABLE_PREFIX%user` (`first`, `default_email_id`)
+    SELECT MAX(`name`), A2.`id`
     FROM `%TABLE_PREFIX%ticket` A1
-        INNER JOIN `%TABLE_PREFIX%dynamic_form_entry` A2 ON (A1.`ticket_id`
-                = A2.`ticket_id` AND A2.`section_id` = 2),
-        INNER JOIN `%TABLE_PREFIX%dynamic_form_field` A3 ON (A2.`section_id`
-                = A3.`section_id`)
-    WHERE A3.`name` = 'subject';
+        INNER JOIN `%TABLE_PREFIX%user_email` A2 ON (A1.`email` = A2.`address`);
+    GROUP BY A2.`id`
+
+--      - Now link the user and user_email tables
+ALTER TABLE `%TABLE_PREFIX%user` ADD KEY `def_eml_id` (`default_email_id`, `id`);
+UPDATE `%TABLE_PREFIX%user_email` A1
+    SET user_id = (
+        SELECT A2.`id` FROM `%TABLE_PREFIX%user` A2
+        WHERE `default_email_id` = A1.`id`);
+ALTER TABLE `%TABLE_PREFIX%user` DROP INDEX `def_eml_id`;
+
+--      - Update the ticket table
+ALTER TABLE `%TABLE_PREFIX%ticket`
+    ADD `user_id` int(11) UNSIGNED NOT NULL DEFAULT 0 AFTER `ticket_id`,
+    ADD `user_email_id` int(11) UNSIGNED NOT NULL DEFAULT 0 AFTER `user_id`;
+
+UPDATE `%TABLE_PREFIX%ticket` A1
+    JOIN `%TABLE_PREFIX%user_email` A2 ON A2.`address` = A1.`email`
+    SET `user_id` = A2.`user_id`,
+        `user_email_id` = A2.`id`;
 
 -- 4. Remove columns from ticket table
 ALTER TABLE `%TABLE_PREFIX%ticket`
diff --git a/include/upgrader/streams/core/d51f303a-DYNAMICF.task.php b/include/upgrader/streams/core/d51f303a-DYNAMICF.task.php
new file mode 100644
index 0000000000000000000000000000000000000000..98d128fbdaed3a1a4a57fe3af06cd0fbb99c591e
--- /dev/null
+++ b/include/upgrader/streams/core/d51f303a-DYNAMICF.task.php
@@ -0,0 +1,21 @@
+<?php
+
+/*
+ * Loads the initial data for dynamic forms into the system. This is
+ * preferred over providing the data inside the SQL scripts
+ */
+
+class DynamicFormLoader extends MigrationTask {
+    var $description = "Loading initial data for dynamic forms";
+
+    function run($max_time) {
+        $i18n = new Internationalization('en_US');
+        $forms = $i18n->getTemplate('form.yaml')->getData();
+        foreach ($forms as $f)
+            DynamicForm::create($f);
+    }
+}
+
+return 'DynamicFormLoader';
+
+?>
diff --git a/open.php b/open.php
index 1d1511c3a70d932e393a743319dc3b16a5a204c7..1370e4c9c9581c459bbc034435f0ce7840b77f48 100644
--- a/open.php
+++ b/open.php
@@ -30,24 +30,15 @@ if($_POST):
             $errors['captcha']='Invalid - try again!';
     }
 
-    $interest=array('name','email','subject');
-    if ($topic=Topic::lookup($vars['topicId'])) {
-        $form=DynamicForm::lookup($topic->ht['form_id'])->instanciate();
-        # Collect name, email, and subject address for banning and such
-        foreach ($form->getAnswers() as $answer) {
-            $fname = $answer->getField()->get('name');
-            if (in_array($fname, $interest))
-                # XXX: Assigning to _POST not considered great PHP
-                #      coding style
-                $vars[$fname] = $answer->getField()->getClean();
+    $interest = array('subject');
+    if ($topic = Topic::lookup($vars['topicId'])) {
+        if ($form = DynamicForm::lookup($topic->ht['form_id'])) {
+            $form = $form->instanciate();
+            if (!$form->isValid())
+                $errors += $form->errors();
         }
-        if (!$form->isValid())
-            $errors = array_merge($errors, $form->errors());
     }
-    $form2 = UserForm::getStaticForm();
-    if (!$form2->isValid())
-        $errors += $form2->errors();
-    if(!$errors && $cfg->allowOnlineAttachments() && $_FILES['attachments'])
+    if (!$errors && $cfg->allowOnlineAttachments() && $_FILES['attachments'])
         $vars['files'] = AttachmentFile::format($_FILES['attachments'], true);
 
     //Ticket::create...checks for errors..
@@ -55,9 +46,11 @@ if($_POST):
         $msg='Support ticket request created';
         Draft::deleteForNamespace('ticket.client.'.substr(session_id(), -12));
         # TODO: Save dynamic form(s)
-        $form->setTicketId($ticket->getId());
-        $form->save();
-        $ticket->loadDynamicData();
+        if (isset($form)) {
+            $form->setTicketId($ticket->getId());
+            $form->save();
+            $ticket->loadDynamicData();
+        }
         //Logged in...simply view the newly created ticket.
         if($thisclient && $thisclient->isValid()) {
             if(!$cfg->showRelatedTickets())
diff --git a/scp/css/scp.css b/scp/css/scp.css
index 8f58ac452a133ffc7b8afd98616a5c81ed9dd366..070aafeeb36dc4f87fc8e40ce13c9a0c55cc7cdf 100644
--- a/scp/css/scp.css
+++ b/scp/css/scp.css
@@ -567,23 +567,27 @@ a.print {
     border:1px solid #f00;
 }
 
-.form_table th {
+.form_table th, div.section-break {
     text-align:left;
     border:1px solid #ccc;
     background:#eee;
     padding:0;
+    padding:5px;
+}
+
+div.section-break h3 {
+    margin: 0;
+    padding: 0;
 }
 
 .form_table th h4 {
     margin:0;
-    padding:5px;
     color:#fff;
     background:#929292;
 }
 
 .form_table th em {
     display:block;
-    padding:5px;
     color:#000;
 }
 
@@ -1197,13 +1201,13 @@ time {
     background:#f8f8f8;
     border:2px solid #2a67ac;
     display:none;
-    z-index:1200;
+    z-index:5;
     box-shadow: 0 5px 60px #001;
     border-radius: 5px;
 }
 
 .redactor_air {
-    z-index: 1201 !important;
+    z-index: 6 !important;
 }
 
 .dialog#advanced-search {
@@ -1405,7 +1409,7 @@ ul.progress li.no small {color:red;}
     width: 100%;
     height: 100%;
     background: #000;
-    z-index: 1000;
+    z-index: 1;
     -webkit-transform: translate3d(0,0,0);
 }
 
diff --git a/scp/forms.php b/scp/forms.php
index 399d8b5d0266cb593ffed9dd38bd2613afbbb641..a26aa7dfc155774f69d12ee46fc0e965a121a148 100644
--- a/scp/forms.php
+++ b/scp/forms.php
@@ -18,20 +18,24 @@ if($_POST) {
                 $form->save();
             foreach ($form->getDynamicFields() as $field) {
                 $id = $field->get('id');
-                if ($_POST["delete-$id"] == 'on') {
+                if ($_POST["delete-$id"] == 'on' && $field->isDeletable()) {
                     $field->delete();
+                    // Don't bother updating the field
                     continue;
                 }
-                foreach (array('sort','label','type','name') as $f)
-                    if (isset($_POST["$f-$id"]))
-                        $field->set($f, $_POST["$f-$id"]);
+                if (isset($_POST["type-$id"]) && $field->isChangeable())
+                    $field->set('type', $_POST["type-$id"]);
+                if (isset($_POST["name-$id"]) && $field->isNameEditable())
+                    $field->set('name', $_POST["name-$id"]);
                 # TODO: make sure all help topics still have all required fields
-                $field->set('required', $_POST["required-$id"] == 'on' ?  1 : 0);
-                $field->set('private', $_POST["private-$id"] == 'on' ?  1 : 0);
-                # Core fields are forced required and public
-                if (in_array($field->get('name'), $required)) {
-                    $field->set('required', 1);
-                    $field->set('private', 0);
+                if (!$field->isRequirementForced())
+                    $field->set('required', $_POST["required-$id"] == 'on' ?  1 : 0);
+                if (!$field->isPrivacyForced())
+                    $field->set('private', $_POST["private-$id"] == 'on' ?  1 : 0);
+                foreach (array('sort','label') as $f) {
+                    if (isset($_POST["$f-$id"])) {
+                        $field->set($f, $_POST["$f-$id"]);
+                    }
                 }
                 if ($field->isValid())
                     $field->save();
diff --git a/scp/tickets.php b/scp/tickets.php
index 296bbc7b273159e7f42127ed0d874d36cc65e97b..c5ade077c4704b8c3b8a00f49785016af493e1cd 100644
--- a/scp/tickets.php
+++ b/scp/tickets.php
@@ -205,7 +205,6 @@ if($_POST && !$errors):
                 $_REQUEST['a'] = null; //Clear edit action - going back to view.
                 //Check to make sure the staff STILL has access post-update (e.g dept change).
                 foreach ($forms as $f) $f->save();
-                $ticket->setOwner($form2->getClean());
                 if(!$ticket->checkStaffAccess($thisstaff))
                     $ticket=null;
             } elseif(!$errors['err']) {
@@ -460,22 +459,20 @@ if($_POST && !$errors):
                 $ticket=null;
                 $interest=array('name','email','subject');
                 if ($topic=Topic::lookup($_POST['topicId'])) {
-                    $form = DynamicForm::lookup($topic->ht['form_id']);
-                    $form = $form->instanciate();
-                    # Collect name, email, and subject address for banning and such
-                    foreach ($form->getAnswers() as $answer) {
-                        $fname = $answer->getField()->get('name');
-                        if (in_array($fname, $interest))
-                            # XXX: Assigning to _POST not considered great PHP
-                            #      coding style
-                            $_POST[$fname] = $answer->getField()->getClean();
+                    if ($form = DynamicForm::lookup($topic->ht['form_id'])) {
+                        $form = $form->instanciate();
+                        # Collect name, email, and subject address for banning and such
+                        foreach ($form->getAnswers() as $answer) {
+                            $fname = $answer->getField()->get('name');
+                            if (in_array($fname, $interest))
+                                # XXX: Assigning to _POST not considered great PHP
+                                #      coding style
+                                $_POST[$fname] = $answer->getField()->getClean();
+                        }
+                        if (!$form->isValid())
+                            $errors = array_merge($errors, $form->errors());
                     }
-                    if (!$form->isValid())
-                        $errors = array_merge($errors, $form->errors());
                 }
-                $form2 = UserForm::getStaticForm();
-                if (!$form2->isValid())
-                    $errors = array_merge($errors, $form2->errors());
                 if(!$thisstaff || !$thisstaff->canCreateTickets()) {
                      $errors['err']='You do not have permission to create tickets. Contact admin for such access';
                 } else {
@@ -487,9 +484,11 @@ if($_POST && !$errors):
                         $msg='Ticket created successfully';
                         $_REQUEST['a']=null;
                         # TODO: Save dynamic form(s)
-                        $form->setTicketId($ticket->getId());
-                        $form->save();
-                        $ticket->loadDynamicData();
+                        if (isset($form)) {
+                            $form->setTicketId($ticket->getId());
+                            $form->save();
+                            $ticket->loadDynamicData();
+                        }
                         if(!$ticket->checkStaffAccess($thisstaff) || $ticket->isClosed())
                             $ticket=null;
                         Draft::deleteForNamespace('ticket.staff%', $thisstaff->getId());
diff --git a/setup/inc/streams/core/install-mysql.sql b/setup/inc/streams/core/install-mysql.sql
index 8faf7ac5c8ac6144c53cb58eec674aea6564db2c..78d3a68c539b938ed128f383b9e0ca6ad1c6502c 100644
--- a/setup/inc/streams/core/install-mysql.sql
+++ b/setup/inc/streams/core/install-mysql.sql
@@ -173,7 +173,8 @@ 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,
-    `type` char(1) NOT NULL DEFAULT 'T',
+    `type` char(1) NOT NULL DEFAULT 'G',
+    `deletable` tinyint(1) NOT NULL DEFAULT 1,
     `title` varchar(255) NOT NULL,
     `instructions` varchar(512),
     `notes` text,
@@ -190,6 +191,7 @@ CREATE TABLE `%TABLE_PREFIX%form_field` (
     `label` varchar(255) NOT NULL,
     `required` tinyint(1) NOT NULL DEFAULT 0,
     `private` tinyint(1) NOT NULL DEFAULT 0,
+    `edit_mask` tinyint(1) NOT NULL DEFAULT 1,
     `name` varchar(64) NOT NULL,
     `configuration` text,
     `sort` int(11) unsigned NOT NULL,
@@ -218,6 +220,7 @@ CREATE TABLE `%TABLE_PREFIX%form_entry_values` (
     `entry_id` int(11) unsigned NOT NULL,
     `field_id` int(11) unsigned NOT NULL,
     `value` text,
+    `value_id` int(11),
     PRIMARY KEY (`entry_id`, `field_id`)
 ) DEFAULT CHARSET=utf8;
 
@@ -349,7 +352,7 @@ DROP TABLE IF EXISTS `%TABLE_PREFIX%filter_rule`;
 CREATE TABLE `%TABLE_PREFIX%filter_rule` (
   `id` int(11) unsigned NOT NULL auto_increment,
   `filter_id` int(10) unsigned NOT NULL default '0',
-  `what` enum('name','email','subject','body','header') NOT NULL,
+  `what` varchar(32) NOT NULL,
   `how` enum('equal','not_equal','contains','dn_contain','starts','ends') NOT NULL,
   `val` varchar(255) NOT NULL,
   `isactive` tinyint(1) unsigned NOT NULL DEFAULT '1',
@@ -756,8 +759,7 @@ DROP TABLE IF EXISTS `%TABLE_PREFIX%user`;
 CREATE TABLE `%TABLE_PREFIX%user` (
   `id` int(10) unsigned NOT NULL auto_increment,
   `default_email_id` int(10) NOT NULL,
-  `first` varchar(64) NOT NULL,
-  `last` varchar(64),
+  `name` varchar(128) NOT NULL,
   `created` datetime NOT NULL,
   `updated` datetime NOT NULL,
   PRIMARY KEY  (`id`)