diff --git a/include/class.dynamic_forms.php b/include/class.dynamic_forms.php
index e2a282e826813720e70208584b26af1554e10142..4fb451490961358f22b86b895c0e59f9097bd36b 100644
--- a/include/class.dynamic_forms.php
+++ b/include/class.dynamic_forms.php
@@ -1290,6 +1290,42 @@ class SelectionField extends FormField {
         }
         return $data;
     }
+
+    function getSearchMethods() {
+        return array(
+            'set' =>        __('has a value'),
+            'notset' =>     __('does not have a value'),
+            'includes' =>   __('includes'),
+            '!includes' =>  __('does not include'),
+        );
+    }
+
+    function getSearchMethodWidgets() {
+        return array(
+            'set' => null,
+            'notset' => null,
+            'includes' => array('ChoiceField', array(
+                'choices' => $this->getChoices(),
+                'configuration' => array('multiselect' => true),
+            )),
+            '!includes' => array('ChoiceField', array(
+                'choices' => $this->getChoices(),
+                'configuration' => array('multiselect' => true),
+            )),
+        );
+    }
+
+    function getSearchQ($method, $value, $name=false) {
+        $name = $name ?: $this->get('name');
+        switch ($method) {
+        case '!includes':
+            return Q::not(array("{$name}__in" => array_keys($value)));
+        case 'includes':
+            return new Q(array("{$name}__in" => array_keys($value)));
+        default:
+            return parent::getSearchQ($method, $value, $name);
+        }
+    }
 }
 
 class TypeaheadSelectionWidget extends ChoicesWidget {
diff --git a/include/class.forms.php b/include/class.forms.php
index e929c8450e5b471af34aa13b8d577cc488a43f05..fb0dfff84710b77122f884001cf3b1b6a8011194 100644
--- a/include/class.forms.php
+++ b/include/class.forms.php
@@ -504,7 +504,7 @@ class FormField {
     function getSearchMethods() {
         return array(
             'set' =>        __('has a value'),
-            'set.not' =>    __('does not have a value'),
+            'notset' =>    __('does not have a value'),
             'equal' =>      __('is'),
             'equal.not' =>  __('is not'),
             'contains' =>   __('contains'),
@@ -515,7 +515,7 @@ class FormField {
     function getSearchMethodWidgets() {
         return array(
             'set' => null,
-            'set.not' => null,
+            'notset' => null,
             'equal' => array('TextboxField', array()),
             'equal.not' => array('TextboxField', array()),
             'contains' => array('TextboxField', array()),
@@ -527,6 +527,39 @@ class FormField {
         );
     }
 
+    /**
+     * This is used by the searching system to build a query for the search
+     * engine. The function should return a criteria listing to match
+     * content saved by the field by the `::to_database()` function.
+     */
+    function getSearchQ($method, $value, $name=false) {
+        $criteria = array();
+        $Q = new Q();
+        $name = $name ?: $this->get('name');
+        switch ($method) {
+            case 'notset':
+                $Q->negate();
+            case 'set':
+                $criteria[$name . '__isnull'] = false;
+                break;
+
+            case 'equal.not':
+                $Q->negate();
+            case 'equal':
+                $criteria[$name . '__eq'] = $value;
+                break;
+
+            case 'contains':
+                $criteria[$name . '__contains'] = $value;
+                break;
+
+            case 'match':
+                $criteria[$name . '__regex'] = $value;
+                break;
+        }
+        return $Q->add($criteria);
+    }
+
     function getSearchWidget($method) {
         $methods = $this->getSearchMethodWidgets();
         $info = $methods[$method];
@@ -537,24 +570,6 @@ class FormField {
         return $info;
     }
 
-    /**
-     * This is used by the searching system to build a query for the search
-     * engine. The function should return a criteria listing to match
-     * content saved by the field by the `::to_database()` function.
-     */
-    function getSearchCriteria($method, $name, $value) {
-        switch ($method) {
-        case 'search':
-            return array($name => $value);
-        case 'search.set':
-            return array("{$name}__isnull" => false);
-        case 'search.notset':
-            return array("{$name}__isnull" => true);
-        default:
-            throw new Exception('Search method not supported by this field');
-        }
-    }
-
     function getLabel() { return $this->get('label'); }
 
     /**
@@ -1179,21 +1194,38 @@ class ChoiceField extends FormField {
     function getSearchMethods() {
         return array(
             'set' =>        __('has a value'),
-            'set.not' =>    __('does not have a value'),
+            'notset' =>     __('does not have a value'),
             'includes' =>   __('includes'),
+            '!includes' =>  __('does not include'),
         );
     }
 
     function getSearchMethodWidgets() {
         return array(
             'set' => null,
-            'set.not' => null,
+            'notset' => null,
             'includes' => array('ChoiceField', array(
                 'choices' => $this->getChoices(),
                 'configuration' => array('multiselect' => true),
             )),
+            '!includes' => array('ChoiceField', array(
+                'choices' => $this->getChoices(),
+                'configuration' => array('multiselect' => true),
+            )),
         );
     }
+
+    function getSearchQ($method, $value, $name=false) {
+        $name = $name ?: $this->get('name');
+        switch ($method) {
+        case '!includes':
+            return Q::not(array("{$name}__in" => array_keys($value)));
+        case 'includes':
+            return new Q(array("{$name}__in" => array_keys($value)));
+        default:
+            return parent::getSearchQ($method, $value, $name);
+        }
+    }
 }
 
 class DatetimeField extends FormField {
@@ -1266,6 +1298,96 @@ class DatetimeField extends FormField {
         elseif ($value === -1 or $value === false)
             $this->_errors[] = __('Enter a valid date');
     }
+
+    function getSearchMethods() {
+        return array(
+            'set' =>        __('has a value'),
+            'notset' =>     __('does not have a value'),
+            'equal' =>      __('on'),
+            'notequal' =>   __('not on'),
+            'before' =>     __('before'),
+            'after' =>      __('after'),
+            'between' =>    __('between'),
+            'ndaysago' =>   __('in the last n days'),
+            'ndays' =>      __('in the next n days'),
+        );
+    }
+
+    function getSearchMethodWidgets() {
+        $config = $this->getConfiguration();
+        return array(
+            'set' => null,
+            'notset' => null,
+            'equal' => array('DatetimeField', array(
+                'configuration' => $config,
+            )),
+            'notequal' => array('DatetimeField', array(
+                'configuration' => $config,
+            )),
+            'before' => array('DatetimeField', array(
+                'configuration' => $config,
+            )),
+            'after' => array('DatetimeField', array(
+                'configuration' => $config,
+            )),
+            'between' => array('InlineformField', array(
+                'form' => array(
+                    'left' => new DatetimeField(),
+                    'text' => new FreeTextField(array(
+                        'configuration' => array('content' => 'and'))
+                    ),
+                    'right' => new DatetimeField(),
+                ),
+            )),
+            'ndaysago' => array('InlineformField', array(
+                'form' => array(
+                    'until' => new TextboxField(array(
+                        'configuration' => array('validator'=>'number', 'size'=>4))
+                    ),
+                    'text' => new FreeTextField(array(
+                        'configuration' => array('content' => 'days'))
+                    ),
+                ),
+            )),
+            'ndays' => array('InlineformField', array(
+                'form' => array(
+                    'until' => new TextboxField(array(
+                        'configuration' => array('validator'=>'number', 'size'=>4))
+                    ),
+                    'text' => new FreeTextField(array(
+                        'configuration' => array('content' => 'days'))
+                    ),
+                ),
+            )),
+        );
+    }
+
+    function getSearchQ($method, $value, $name=false) {
+        $name = $name ?: $this->get('name');
+        switch ($method) {
+        case 'after':
+            return new Q(array("{$name}__gte" => $value));
+        case 'before':
+            return new Q(array("{$name}__lt" => $value));
+        case 'between':
+            return new Q(array(
+                "{$name}__gte" => $value['left'],
+                "{$name}__lte" => $value['right'],
+            ));
+        case 'ndaysago':
+            return new Q(array(
+                "{$name}__lt" => SqlFunction::NOW(),
+                "{$name}__gte" => SqlExpression::minus(SqlFunction::NOW(), SqlInterval::DAY($value['until'])),
+            ));
+        case 'ndays':
+            return new Q(array(
+                "{$name}__gt" => SqlFunction::NOW(),
+                "{$name}__lte" => SqlExpression::plus(SqlFunction::NOW(), SqlInterval::DAY($value['until'])),
+            ));
+        default:
+            return parent::getSearchQ($method, $value, $name);
+        }
+    }
 }
 
 /**
@@ -1849,7 +1971,7 @@ class InlineFormField extends FormField {
 
     function validateEntry($value) {
         if (!$this->getInlineForm()->isValid()) {
-            $this->_errors[] = 'Correct errors in the inline form';
+            $this->_errors[] = __('Correct errors in the inline form');
         }
     }
 
@@ -2006,7 +2128,6 @@ class TextboxWidget extends Widget {
         $placeholder = sprintf('placeholder="%s"', $this->field->getLocal('placeholder',
             $config['placeholder']));
         ?>
-        <span style="display:inline-block">
         <input type="<?php echo static::$input_type; ?>"
             id="<?php echo $this->id; ?>"
             <?php echo implode(' ', array_filter(array(
@@ -2014,7 +2135,6 @@ class TextboxWidget extends Widget {
                 $translatable, $placeholder))); ?>
             name="<?php echo $this->name; ?>"
             value="<?php echo Format::htmlchars($this->value); ?>"/>
-        </span>
         <?php
     }
 }