diff --git a/include/ajax.tickets.php b/include/ajax.tickets.php
index 3d93e9a9cf3585d38189e8c705dafe54246fa2aa..7d0ee9eb7f5e9c28aa7aa54dea466228946046db 100644
--- a/include/ajax.tickets.php
+++ b/include/ajax.tickets.php
@@ -208,13 +208,16 @@ class TicketsAjaxAPI extends AjaxController {
         $cdata_search = false;
         foreach (TicketForm::getInstance()->getFields() as $f) {
             if (isset($req[$f->getFormName()])
-                    && ($val = $req[$f->getFormName()])
-                    && strlen(trim($val))) {
+                    && ($val = $req[$f->getFormName()])) {
                 $name = $f->get('name') ? $f->get('name')
                     : 'field_'.$f->get('id');
-                if ($f->getImpl()->hasIdValue() && is_numeric($val)) {
-                    $cwhere = "cdata.`{$name}_id` = ".db_input($val);
-                    $criteria["cdata.{$name}_id"] = $val;
+                if (is_array($val)) {
+                    $cwhere = '(' . implode(' OR ', array_map(
+                        function($k) use ($name) {
+                            return sprintf('FIND_IN_SET(%s, `%s`)', db_input($k), $name);
+                        }, $val)
+                    ) . ')';
+                    $criteria["cdata.{$name}"] = $val;
                 }
                 else {
                     $cwhere = "cdata.`$name` LIKE '%".db_real_escape($val)."%'";
@@ -262,8 +265,8 @@ class TicketsAjaxAPI extends AjaxController {
             $uid = md5($_SERVER['QUERY_STRING']);
             $_SESSION["adv_$uid"] = $tickets;
             $result['success'] = sprintf(__("Search criteria matched %s"),
-                    sprintf(_N('%d ticket', '%d tickets'), count($tickets),
-                        $tickets))
+                    sprintf(_N('%d ticket', '%d tickets', count($tickets)), count($tickets)
+                ))
                 . " - <a href='tickets.php?advsid=$uid'>".__('view')."</a>";
         } else {
             $result['fail']=__('No tickets found matching your search criteria.');
diff --git a/include/class.dynamic_forms.php b/include/class.dynamic_forms.php
index 5a2ecec0b2ea62290152a9d755253cd00cf4f290..c5133c9d6a40e81df5c47406c13e6fe38565f617 100644
--- a/include/class.dynamic_forms.php
+++ b/include/class.dynamic_forms.php
@@ -196,16 +196,19 @@ class DynamicForm extends VerySimpleModel {
             if (!$impl->hasData() || $impl->isPresentationOnly())
                 continue;
 
+            $id = $f->get('id');
             $name = ($f->get('name')) ? $f->get('name')
-                : 'field_'.$f->get('id');
+                : 'field_'.$id;
 
-            $fields[] = sprintf(
-                'MAX(IF(field.name=\'%1$s\',ans.value,NULL)) as `%1$s`',
-                $name);
-            if ($impl->hasIdValue()) {
+            if ($impl instanceof ChoiceField || $impl instanceof SelectionField) {
                 $fields[] = sprintf(
-                    'MAX(IF(field.name=\'%1$s\',ans.value_id,NULL)) as `%1$s_id`',
-                    $name);
+                    'MAX(CASE WHEN field.id=\'%1$s\' THEN REPLACE(REPLACE(REPLACE(REPLACE(coalesce(ans.value_id, ans.value), \'{\', \'\'), \'}\', \'\'), \'"\', \'\'), \':\', \',\') ELSE NULL END) as `%2$s`',
+                    $id, $name);
+            }
+            else {
+                $fields[] = sprintf(
+                    'MAX(IF(field.id=\'%1$s\',coalesce(ans.value_id, ans.value),NULL)) as `%2$s`',
+                    $id, $name);
             }
         }
         return $fields;
@@ -249,8 +252,8 @@ Filter::addSupportedMatches(/* @trans */ 'User Data', function() {
         if (!$f->hasData())
             continue;
         $matches['field.'.$f->get('id')] = __('User').' / '.$f->getLabel();
-        if (($fi = $f->getImpl()) instanceof SelectionField) {
-            foreach ($fi->getList()->getForm()->getFields() as $p) {
+        if (($fi = $f->getImpl()) && $fi->hasSubFields()) {
+            foreach ($fi->getSubFields() as $p) {
                 $matches['field.'.$f->get('id').'.'.$p->get('id')]
                     = __('User').' / '.$f->getLabel().' / '.$p->getLabel();
             }
@@ -321,10 +324,8 @@ class TicketForm extends DynamicForm {
         $f = $answer->getField();
         $name = $f->get('name') ? $f->get('name')
             : 'field_'.$f->get('id');
-        $ids = $f->hasIdValue();
-        $fields = sprintf('`%s`=', $name) . db_input($answer->get('value'));
-        if ($f->hasIdValue())
-            $fields .= sprintf(',`%s_id`=', $name) . db_input($answer->getIdValue());
+        $fields = sprintf('`%s`=', $name) . db_input(
+            implode(',', $answer->getSearchKeys()));
         $sql = 'INSERT INTO `'.TABLE_PREFIX.'ticket__cdata` SET '.$fields
             .', `ticket_id`='.db_input($answer->getEntry()->get('object_id'))
             .' ON DUPLICATE KEY UPDATE '.$fields;
@@ -339,8 +340,8 @@ Filter::addSupportedMatches(/* @trans */ 'Ticket Data', function() {
         if (!$f->hasData())
             continue;
         $matches['field.'.$f->get('id')] = __('Ticket').' / '.$f->getLabel();
-        if (($fi = $f->getImpl()) instanceof SelectionField) {
-            foreach ($fi->getList()->getForm()->getFields() as $p) {
+        if (($fi = $f->getImpl()) && $fi->hasSubFields()) {
+            foreach ($fi->getSubFields() as $p) {
                 $matches['field.'.$f->get('id').'.'.$p->get('id')]
                     = __('Ticket').' / '.$f->getLabel().' / '.$p->getLabel();
             }
@@ -381,8 +382,8 @@ Filter::addSupportedMatches(/* trans */ 'Custom Forms', function() {
             if (!$f->hasData())
                 continue;
             $matches['field.'.$f->get('id')] = $form->getTitle().' / '.$f->getLabel();
-            if (($fi = $f->getImpl()) instanceof SelectionField) {
-                foreach ($fi->getList()->getProperties() as $p) {
+            if (($fi = $f->getImpl()) && $fi->hasSubFields()) {
+                foreach ($fi->getSubFields() as $p) {
                     $matches['field.'.$f->get('id').'.'.$p->get('id')]
                         = $form->getTitle().' / '.$f->getLabel().' / '.$p->getLabel();
                 }
@@ -448,7 +449,7 @@ class DynamicFormField extends VerySimpleModel {
      */
     function setConfiguration(&$errors=array()) {
         $config = array();
-        foreach ($this->getConfigurationForm() as $name=>$field) {
+        foreach ($this->getConfigurationForm($_POST)->getFields() as $name=>$field) {
             $config[$name] = $field->to_php($field->getClean());
             $errors = array_merge($errors, $field->errors());
         }
@@ -944,6 +945,14 @@ class DynamicFormEntryAnswer extends VerySimpleModel {
         );
     }
 
+    function getSearchKeys() {
+        $val = $this->getField()->to_php($this->getValue());
+        if (is_array($val))
+            return array_keys($val);
+
+        return [$val];
+    }
+
     function asVar() {
         return (is_object($this->getValue()))
             ? $this->getValue() : $this->toString();
@@ -1004,15 +1013,13 @@ class SelectionField extends FormField {
     }
 
     function to_database($value) {
-        $id = null;
         if (is_array($value)) {
             reset($value);
-            $id = key($value);
         }
         if ($value && is_array($value))
             $value = JsonDataEncoder::encode($value);
 
-        return array($value, $id);
+        return $value;
     }
 
     function to_php($value, $id=false) {
@@ -1023,12 +1030,17 @@ class SelectionField extends FormField {
             if (isset($choices[$value]))
                 $value = $choices[$value];
         }
+        // Don't set the ID here as multiselect prevents using exactly one
+        // ID value. Instead, stick with the JSON value only.
         return $value;
     }
 
-    function hasIdValue() {
+    function hasSubFields() {
         return true;
     }
+    function getSubFields() {
+        return $this->getConfigurationForm()->getFields();
+    }
 
     function toString($items) {
         return ($items && is_array($items))
diff --git a/include/class.forms.php b/include/class.forms.php
index aa9405d80d056e0e8764c0b330d7469e14426798..040cd255425d55c5a24140bbda9da0cffa463606 100644
--- a/include/class.forms.php
+++ b/include/class.forms.php
@@ -28,8 +28,11 @@ class Form {
 
     function __construct($fields=array(), $source=null, $options=array()) {
         $this->fields = $fields;
-        foreach ($fields as $f)
+        foreach ($fields as $k=>$f) {
             $f->setForm($this);
+            if (!$f->get('name') && $k)
+                $f->set('name', $k);
+        }
         if (isset($options['title']))
             $this->title = $options['title'];
         if (isset($options['instructions']))
@@ -535,12 +538,44 @@ class FormField {
         return false;
     }
 
-    function getConfigurationForm() {
+    /**
+     * Indicates if the field has subfields accessible via getSubFields()
+     * method. Useful for filter integration. Should connect with
+     * getFilterData()
+     */
+    function hasSubFields() {
+        return false;
+    }
+    function getSubFields() {
+        return null;
+    }
+
+    /**
+     * Indicates if the field provides for searching for something other
+     * than keywords. For instance, textbox fields can have hits by keyword
+     * searches alone, but selection fields should provide the option to
+     * match a specific value or set of values and therefore need to
+     * participate on any search builder.
+     */
+    function hasSpecialSearch() {
+        return true;
+    }
+
+    function getConfigurationForm($source=null) {
         if (!$this->_cform) {
             $type = static::getFieldType($this->get('type'));
             $clazz = $type[1];
             $T = new $clazz();
-            $this->_cform = $T->getConfigurationOptions();
+            $config = $this->getConfiguration();
+            $this->_cform = new Form($T->getConfigurationOptions(), $source);
+            if (!$source && $config) {
+                foreach ($this->_cform->getFields() as $name=>$f) {
+                    if (isset($config[$name]))
+                        $f->value = $config[$name];
+                    elseif ($f->get('default'))
+                        $f->value = $f->get('default');
+                }
+            }
         }
         return $this->_cform;
     }
@@ -597,6 +632,10 @@ class TextboxField extends FormField {
         );
     }
 
+    function hasSpecialSearch() {
+        return false;
+    }
+
     function validateEntry($value) {
         parent::validateEntry($value);
         $config = $this->getConfiguration();
@@ -661,6 +700,10 @@ class TextareaField extends FormField {
         );
     }
 
+    function hasSpecialSearch() {
+        return false;
+    }
+
     function display($value) {
         $config = $this->getConfiguration();
         if ($config['html'])
@@ -705,6 +748,10 @@ class PhoneField extends FormField {
         );
     }
 
+    function hasSpecialSearch() {
+        return false;
+    }
+
     function validateEntry($value) {
         parent::validateEntry($value);
         $config = $this->getConfiguration();
@@ -990,6 +1037,9 @@ class ThreadEntryField extends FormField {
     function isPresentationOnly() {
         return true;
     }
+    function hasSpecialSearch() {
+        return false;
+    }
 
     function getConfigurationOptions() {
         global $cfg;
@@ -1302,6 +1352,10 @@ class FileUploadField extends FormField {
         );
     }
 
+    function hasSpecialSearch() {
+        return false;
+    }
+
     /**
      * Called from the ajax handler for async uploads via web clients.
      */
@@ -1634,7 +1688,7 @@ class ChoicesWidget extends Widget {
 
     function render($mode=false) {
 
-        if ($mode && $mode == 'view') {
+        if ($mode == 'view') {
             if (!($val = (string) $this->field))
                 $val = sprintf('<span class="faded">%s</span>', __('None'));
 
@@ -1643,6 +1697,10 @@ class ChoicesWidget extends Widget {
         }
 
         $config = $this->field->getConfiguration();
+        if ($mode == 'search') {
+            $config['multiselect'] = true;
+        }
+
         // Determine the value for the default (the one listed if nothing is
         // selected)
         $choices = $this->field->getChoices(true);
diff --git a/include/class.list.php b/include/class.list.php
index b4423c3aab51662afb6cb316582607ed81f3b50b..98be809500197f76c0265086236ee4b665ad4adb 100644
--- a/include/class.list.php
+++ b/include/class.list.php
@@ -66,7 +66,7 @@ interface CustomListItem {
     function getSortOrder();
 
     function getConfiguration();
-    function getConfigurationForm();
+    function getConfigurationForm($source=null);
 
 
     function isEnabled();
@@ -538,9 +538,8 @@ class DynamicListItem extends VerySimpleModel implements CustomListItem {
 
     function setConfiguration(&$errors=array()) {
         $config = array();
-        foreach ($this->getConfigurationForm()->getFields() as $field) {
-            $val = $field->to_database($field->getClean());
-            $config[$field->get('id')] = is_array($val) ? $val[1] : $val;
+        foreach ($this->getConfigurationForm($_POST)->getFields() as $field) {
+            $config[$field->get('id')] = $field->to_php($field->getClean());
             $errors = array_merge($errors, $field->errors());
         }
         if (count($errors) === 0)
@@ -549,10 +548,20 @@ class DynamicListItem extends VerySimpleModel implements CustomListItem {
         return count($errors) === 0;
     }
 
-    function getConfigurationForm() {
+    function getConfigurationForm($source=null) {
         if (!$this->_form) {
+            $config = $this->getConfiguration();
             $this->_form = DynamicForm::lookup(
-                array('type'=>'L'.$this->get('list_id')));
+                array('type'=>'L'.$this->get('list_id')))->getForm($source);
+            if (!$source && $config) {
+                foreach ($this->_form->getFields() as $f) {
+                    $name = $f->get('id');
+                    if (isset($config[$name]))
+                        $f->value = $f->to_php($config[$name]);
+                    else if ($f->get('default'))
+                        $f->value = $f->get('default');
+                }
+            }
         }
         return $this->_form;
     }
@@ -788,19 +797,31 @@ class TicketStatus  extends VerySimpleModel implements CustomListItem {
     }
 
     function getForm() {
-        return $this->getConfigurationForm();
-    }
-
-    function getConfigurationForm() {
-
         if (!$this->_form && $this->_list) {
             $this->_form = DynamicForm::lookup(
-                    array('type'=>'L'.$this->_list->getId()));
+                array('type'=>'L'.$this->_list->getId()));
         }
-
         return $this->_form;
     }
 
+    function getConfigurationForm($source=null) {
+
+        if ($form = $this->getForm()) {
+            $config = $this->getConfiguration();
+            $form = $form->getForm($source);
+            if (!$source && $config) {
+                foreach ($form->getFields() as $f) {
+                    $name = $f->get('id');
+                    if (isset($config[$name]))
+                        $f->value = $f->to_php($config[$name]);
+                    else if ($f->get('default'))
+                        $f->value = $f->get('default');
+                }
+            }
+        }
+        return $form;
+    }
+
     function isEnabled() {
         return $this->hasFlag('mode', self::ENABLED);
     }
@@ -896,8 +917,8 @@ class TicketStatus  extends VerySimpleModel implements CustomListItem {
             if (!$this->_settings)
                 $this->_settings = array();
 
-            if ($this->getConfigurationForm()) {
-                foreach ($this->getConfigurationForm()->getFields() as $f)  {
+            if ($this->getForm()) {
+                foreach ($this->getForm()->getFields() as $f)  {
                     $name = mb_strtolower($f->get('name'));
                     $id = $f->get('id');
                     switch($name) {
@@ -922,7 +943,7 @@ class TicketStatus  extends VerySimpleModel implements CustomListItem {
 
     function setConfiguration(&$errors=array()) {
         $properties = array();
-        foreach ($this->getConfigurationForm()->getFields() as $f) {
+        foreach ($this->getConfigurationForm($_POST)->getFields() as $f) {
             if ($this->isInternal() //Item is internal.
                     && !$f->isEditable())
                 continue;
diff --git a/include/class.search.php b/include/class.search.php
index 270e0322055ae05714954431ae8c7a812ff2bcf2..9e3a380efbfc081c42fe27ddf5b32a95f7f244d4 100644
--- a/include/class.search.php
+++ b/include/class.search.php
@@ -331,7 +331,17 @@ class MysqlSearchBackend extends SearchBackend {
                             // Search ticket CDATA table
                             $cdata_search = true;
                             $name = substr($name, 6);
-                            $where[] = sprintf("cdata.%s = %s", $name, db_input($value));
+                            if (is_array($value)) {
+                                $where[] = '(' . implode(' OR ', array_map(
+                                    function($k) use ($name) {
+                                        return sprintf('FIND_IN_SET(%s, cdata.`%s`)',
+                                            db_input($k), $name);
+                                    }, $value)
+                                ) . ')';
+                            }
+                            else {
+                                $where[] = sprintf("cdata.%s = %s", $name, db_input($value));
+                            }
                         }
                     }
                 }
diff --git a/include/staff/templates/dynamic-field-config.tmpl.php b/include/staff/templates/dynamic-field-config.tmpl.php
index da2621b3fef930186fe68339b3dbea4b974b9617..af257465289439f8748ffc948911be5fc62e2f1c 100644
--- a/include/staff/templates/dynamic-field-config.tmpl.php
+++ b/include/staff/templates/dynamic-field-config.tmpl.php
@@ -5,15 +5,9 @@
             echo $field->get('id'); ?>">
         <?php
         echo csrf_token();
-        $config = $field->getConfiguration();
-        $form = new Form($field->getConfigurationForm());
+        $form = $field->getConfigurationForm();
         echo $form->getMedia();
-        foreach ($form->getFields() as $name=>$f) {
-            if (isset($config[$name]))
-                $f->value = $config[$name];
-            else if ($f->get('default'))
-                $f->value = $f->get('default');
-            ?>
+        foreach ($form->getFields() as $name=>$f) { ?>
             <div class="flush-left custom-field">
             <div class="field-label">
             <label for="<?php echo $f->getWidget()->name; ?>">
diff --git a/include/staff/templates/list-item-properties.tmpl.php b/include/staff/templates/list-item-properties.tmpl.php
index 44ce0de055728a163cd805e59ec99a5a8e549fd9..51e26b65a55a3a205011cef58951d11280875619 100644
--- a/include/staff/templates/list-item-properties.tmpl.php
+++ b/include/staff/templates/list-item-properties.tmpl.php
@@ -8,13 +8,9 @@
         echo csrf_token();
         $config = $item->getConfiguration();
         $internal = $item->isInternal();
-        foreach ($item->getConfigurationForm()->getFields() as $f) {
-            $name = $f->get('id');
-            if (isset($config[$name]))
-                $f->value = $f->to_php($config[$name]);
-            else if ($f->get('default'))
-                $f->value = $f->get('default');
-            ?>
+        $form = $item->getConfigurationForm();
+        echo $form->getMedia();
+        foreach ($form->getFields() as $f) { ?>
             <div class="custom-field">
             <div class="field-label">
             <label for="<?php echo $f->getWidget()->name; ?>"
diff --git a/include/staff/tickets.inc.php b/include/staff/tickets.inc.php
index 3f828d210662a13a70264c9405069e991b7fe252..671600b785e20d90df39173482da78c8a5803d9f 100644
--- a/include/staff/tickets.inc.php
+++ b/include/staff/tickets.inc.php
@@ -255,7 +255,7 @@ $qselect.=' ,IF(ticket.duedate IS NULL,IF(sla.id IS NULL, NULL, DATE_ADD(ticket.
          .' ,ticket.created as ticket_created, CONCAT_WS(" ", staff.firstname, staff.lastname) as staff, team.name as team '
          .' ,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 '
-         .' ,cdata.priority_id, cdata.subject, pri.priority_desc, pri.priority_color';
+         .' ,cdata.priority as priority_id, cdata.subject, pri.priority_desc, pri.priority_color';
 
 $qfrom.=' 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()).') '
@@ -265,7 +265,7 @@ $qfrom.=' LEFT JOIN '.TICKET_LOCK_TABLE.' tlock ON (ticket.ticket_id=tlock.ticke
        .' LEFT JOIN '.TOPIC_TABLE.' topic ON (ticket.topic_id=topic.topic_id) '
        .' LEFT JOIN '.TOPIC_TABLE.' ptopic ON (ptopic.topic_id=topic.topic_pid) '
        .' LEFT JOIN '.TABLE_PREFIX.'ticket__cdata cdata ON (cdata.ticket_id = ticket.ticket_id) '
-       .' LEFT JOIN '.PRIORITY_TABLE.' pri ON (pri.priority_id = cdata.priority_id)';
+       .' LEFT JOIN '.PRIORITY_TABLE.' pri ON (pri.priority_id = cdata.priority)';
 
 TicketForm::ensureDynamicDataView();
 
@@ -648,9 +648,9 @@ if ($results) {
         $tform = TicketForm::objects()->one();
         echo $tform->getForm()->getMedia();
         foreach ($tform->getInstance()->getFields() as $f) {
-            if (in_array($f->get('type'), array('text', 'memo', 'phone', 'thread')))
+            if (!$f->hasData())
                 continue;
-            elseif (!$f->hasData())
+            elseif (!$f->getImpl()->hasSpecialSearch())
                 continue;
             ?><fieldset class="span6">
             <label><?php echo $f->getLabel(); ?>:</label><div><?php
diff --git a/include/upgrader/streams/core/8f99b8bf-03ff59bf.cleanup.sql b/include/upgrader/streams/core/8f99b8bf-03ff59bf.cleanup.sql
index bccb432fbf33b17fc27d54fc90490fcfb01e7315..dbeb1fa39788d22ebe82ba7ef6c559d2de363bcf 100644
--- a/include/upgrader/streams/core/8f99b8bf-03ff59bf.cleanup.sql
+++ b/include/upgrader/streams/core/8f99b8bf-03ff59bf.cleanup.sql
@@ -4,4 +4,7 @@ DELETE FROM `%TABLE_PREFIX%config`
 ALTER TABLE `%TABLE_PREFIX%ticket`
     DROP COLUMN `status`;
 
+-- Regenerate the CDATA table with the new format for 1.9.4
+DROP TABLE `%TABLE_PREFIX%ticket__cdata`;
+
 OPTIMIZE TABLE `%TABLE_PREFIX%ticket`;