diff --git a/include/ajax.search.php b/include/ajax.search.php
index 684a2e5a43debb86b0f6a50b7d6c182d1b1a7b91..ffb5f9dd4815fef5c6862a7f72d4edc1d0d97914 100644
--- a/include/ajax.search.php
+++ b/include/ajax.search.php
@@ -56,6 +56,7 @@ class SearchAjaxAPI extends AjaxController {
         case ':ticket':
         case ':user':
         case ':organization':
+        case ':field':
             // Support nested field ids for list properties and such
             if (strpos($id, '.') !== false)
                 list(,$id) = explode('!', $id, 2);
@@ -67,7 +68,12 @@ class SearchAjaxAPI extends AjaxController {
         }
 
         self::ensureConsistentFormFieldIds($_GET['ff_uid']);
-        $fields = SavedSearch::getSearchField($field->getImpl(), $name);
+
+        $impl = $field->getImpl();
+        $impl->set('label', sprintf('%s / %s',
+            $field->form->getLocal('title'), $field->getLocal('label')
+        ));
+        $fields = SavedSearch::getSearchField($impl, $name);
         $form = new Form($fields);
         // Check the box to search the field by default
         if ($F = $form->getField("{$name}+search"))
@@ -80,6 +86,8 @@ class SearchAjaxAPI extends AjaxController {
         return $this->encode(array(
             'success' => true,
             'html' => $html,
+            // Send the current formfield UID to be resent with the next
+            // addField request and set above
             'ff_uid' => FormField::$uid,
         ));
     }
@@ -161,6 +169,15 @@ class SearchAjaxAPI extends AjaxController {
                 */
             }
         }
+        $fields = &$matches[__('Custom Forms')];
+        foreach (DynamicForm::objects()->filter(array('type'=>'G')) as $form) {
+            foreach ($form->getFields() as $f) {
+                if (!$f->hasData() || $f->isPresentationOnly())
+                    continue;
+                $key = sprintf(':field!%d', $f->get('id'), $f->get('id'));
+                $fields[$key] = $form->getLocal('title').' / '.$f->getLocal('label');
+            }
+        }
         return $matches;
     }
 
diff --git a/include/class.dynamic_forms.php b/include/class.dynamic_forms.php
index ebe6c6bb01138f642f03c3ac06e9350ec6935f5d..1ff552bd0d80edd5d56242312e1cc8ff4dab00e2 100644
--- a/include/class.dynamic_forms.php
+++ b/include/class.dynamic_forms.php
@@ -412,6 +412,7 @@ class DynamicFormField extends VerySimpleModel {
         'table' => FORM_FIELD_TABLE,
         'ordering' => array('sort'),
         'pk' => array('id'),
+        'select_related' => array('form'),
         'joins' => array(
             'form' => array(
                 'null' => true,
diff --git a/include/class.orm.php b/include/class.orm.php
index 8d32361b8d43272f3cb78d4ef5697ea2106807e4..627338c4c1f48cc21036f1227bd26e709210a670 100644
--- a/include/class.orm.php
+++ b/include/class.orm.php
@@ -62,35 +62,39 @@ class ModelMeta implements ArrayAccess {
         if (!isset($meta['joins']))
             $meta['joins'] = array();
         foreach ($meta['joins'] as $field => &$j) {
-            if (isset($j['reverse'])) {
-                list($fmodel, $key) = explode('.', $j['reverse']);
-                $info = $fmodel::$meta['joins'][$key];
-                $constraint = array();
-                if (!is_array($info['constraint']))
-                    throw new OrmConfigurationException(sprintf(__(
-                        // `reverse` here is the reverse of an ORM relationship
-                        '%s: Reverse does not specify any constraints'),
-                        $j['reverse']));
-                foreach ($info['constraint'] as $foreign => $local) {
-                    list(,$field) = explode('.', $local);
-                    $constraint[$field] = "$fmodel.$foreign";
-                }
-                $j['constraint'] = $constraint;
-                if (!isset($j['list']))
-                    $j['list'] = true;
-                if (!isset($j['null']))
-                    $j['null'] = $info['null'] ?: false;
-            }
-            // XXX: Make this better (ie. composite keys)
-            $keys = array_keys($j['constraint']);
-            $foreign = $j['constraint'][$keys[0]];
-            $j['fkey'] = explode('.', $foreign);
-            $j['local'] = $keys[0];
+            $this->processJoin($j);
         }
         unset($j);
         $this->base = $meta;
     }
 
+    function processJoin(&$j) {
+        if (isset($j['reverse'])) {
+            list($fmodel, $key) = explode('.', $j['reverse']);
+            $info = $fmodel::$meta['joins'][$key];
+            $constraint = array();
+            if (!is_array($info['constraint']))
+                throw new OrmConfigurationException(sprintf(__(
+                    // `reverse` here is the reverse of an ORM relationship
+                    '%s: Reverse does not specify any constraints'),
+                    $j['reverse']));
+            foreach ($info['constraint'] as $foreign => $local) {
+                list(,$field) = explode('.', $local);
+                $constraint[$field] = "$fmodel.$foreign";
+            }
+            $j['constraint'] = $constraint;
+            if (!isset($j['list']))
+                $j['list'] = true;
+            if (!isset($j['null']))
+                $j['null'] = $info['null'] ?: false;
+        }
+        // XXX: Make this better (ie. composite keys)
+        $keys = array_keys($j['constraint']);
+        $foreign = $j['constraint'][$keys[0]];
+        $j['fkey'] = explode('.', $foreign);
+        $j['local'] = $keys[0];
+    }
+
     function offsetGet($field) {
         if (!isset($this->base[$field]))
             $this->setupLazy($field);
diff --git a/include/class.search.php b/include/class.search.php
index b43798495d2847ae270737fe91a7bd54587c05f3..3faf623bbfd7872e807a9528a9f00a7466d5e490 100644
--- a/include/class.search.php
+++ b/include/class.search.php
@@ -681,7 +681,11 @@ class SavedSearch extends VerySimpleModel {
                 }
                 $id = $info[2];
                 if (is_numeric($id) && ($field = DynamicFormField::lookup($id))) {
-                    $core[":{$info[1]}!{$info[2]}"] = $field->getImpl();
+                    $impl = $field->getImpl();
+                    $impl->set('label', sprintf('%s / %s',
+                        $field->form->getLocal('title'), $field->getLocal('label')
+                    ));
+                    $core[":{$info[1]}!{$info[2]}"] = $impl;
                 }
             }
         }
@@ -743,23 +747,26 @@ class SavedSearch extends VerySimpleModel {
                             ':organization' => 'user__org__cdata__',
                         );
                         $column = $field->get('name') ?: 'field_'.$field->get('id');
-                        foreach ($other_paths as $k => $OP) {
-                            if (substr($name, 0, strlen($k)) == $k) {
-                                // XXX: Last mile — find a better idea
-                                switch (array($k, $column)) {
-                                case array(':user', 'name'):
-                                    $name = 'user__name';
-                                    break;
-                                case array(':user', 'email'):
-                                    $name = 'user__emails__address';
-                                    break;
-                                case array(':organization', 'name'):
-                                    $name = 'user__org__name';
-                                    break;
-                                default:
-                                    $name = $OP . $column;
-                                }
-                            }
+                        list($type,$id) = explode('!', $name, 2);
+                        $OP = $other_paths[$type];
+                        if ($type == ':field') {
+                            $DF = DynamicFormField::lookup($id);
+                            TicketModel::registerCustomData($DF->form);
+                            $OP = 'cdata+'.$DF->form->id.'__';
+                        }
+                        // XXX: Last mile — find a better idea
+                        switch (array($type, $column)) {
+                        case array(':user', 'name'):
+                            $name = 'user__name';
+                            break;
+                        case array(':user', 'email'):
+                            $name = 'user__emails__address';
+                            break;
+                        case array(':organization', 'name'):
+                            $name = 'user__org__name';
+                            break;
+                        default:
+                            $name = $OP . $column;
                         }
                     }
 
diff --git a/include/class.ticket.php b/include/class.ticket.php
index 3183caf8242f7f59520ee1431023c58ca04d0a29..25798eada6d869b02fb35770330bf90a618d325a 100644
--- a/include/class.ticket.php
+++ b/include/class.ticket.php
@@ -92,6 +92,43 @@ class TicketModel extends VerySimpleModel {
 
         return false;
     }
+
+    static function registerCustomData(DynamicForm $form) {
+        if (!isset(static::$meta['joins']['cdata+'.$form->id])) {
+            $cdata_class = <<<EOF
+class DynamicForm{$form->id} extends DynamicForm {
+    static function getInstance() {
+        static \$instance;
+        if (!isset(\$instance))
+            \$instance = static::lookup({$form->id});
+        return \$instance;
+    }
+}
+class TicketCdataForm{$form->id} {
+    static \$meta = array(
+        'view' => true,
+        'pk' => array('ticket_id'),
+        'joins' => array(
+            'ticket' => array(
+                'constraint' => array('ticket_id' => 'TicketModel.ticket_id'),
+            ),
+        )
+    );
+    static function getQuery(\$compiler) {
+        return '('.DynamicForm{$form->id}::getCrossTabQuery('T', 'ticket_id').')';
+    }
+}
+EOF;
+            eval($cdata_class);
+            static::$meta['joins']['cdata+'.$form->id] = array(
+                'reverse' => 'TicketCdataForm'.$form->id.'.ticket',
+                'null' => true,
+            );
+            // This may be necessary if the model has already been inspected
+            if (static::$meta instanceof ModelMeta)
+                static::$meta->processJoin(static::$meta['joins']['cdata+'.$form->id]);
+        }
+    }
 }
 
 class TicketCData extends VerySimpleModel {