From c06d79e69bd7b1bb115dc5545474483bf328ae8b Mon Sep 17 00:00:00 2001
From: Jaap Jansma <jaap.jansma@civicoop.org>
Date: Mon, 25 Nov 2019 14:31:07 +0100
Subject: [PATCH] added not set to date filter

---
 .../SqlDataFlow/SimpleWhereClause.php         |  2 +-
 .../FilterHandler/AbstractFilterHandler.php   | 22 +++++++++---
 .../Dataprocessor/Form/Filter/DateRange.tpl   |  5 ++-
 .../Form/Filter/GenericFilter.tpl             | 35 +++++++++----------
 4 files changed, 38 insertions(+), 26 deletions(-)

diff --git a/Civi/DataProcessor/DataFlow/SqlDataFlow/SimpleWhereClause.php b/Civi/DataProcessor/DataFlow/SqlDataFlow/SimpleWhereClause.php
index c3b7d2c8..a936ece6 100644
--- a/Civi/DataProcessor/DataFlow/SqlDataFlow/SimpleWhereClause.php
+++ b/Civi/DataProcessor/DataFlow/SqlDataFlow/SimpleWhereClause.php
@@ -109,7 +109,7 @@ class SimpleWhereClause implements WhereClauseInterface {
         return "(`{$this->table_alias}`.`{$this->field}` {$this->operator} {$this->value} OR `{$this->table_alias}`.`{$this->field}` IS NULL)";
         break;
       case 'IS NULL':
-        return "(`{$this->table_alias}`.`{$this->field}` {$this->operator} OR `{$this->table_alias}`.`{$this->field}` != '')";
+        return "(`{$this->table_alias}`.`{$this->field}` {$this->operator} OR `{$this->table_alias}`.`{$this->field}` = '')";
         break;
       case 'IS NOT NULL':
         return "(`{$this->table_alias}`.`{$this->field}` {$this->operator} AND `{$this->table_alias}`.`{$this->field}` != '')";
diff --git a/Civi/DataProcessor/FilterHandler/AbstractFilterHandler.php b/Civi/DataProcessor/FilterHandler/AbstractFilterHandler.php
index eebd9717..bfa95219 100644
--- a/Civi/DataProcessor/FilterHandler/AbstractFilterHandler.php
+++ b/Civi/DataProcessor/FilterHandler/AbstractFilterHandler.php
@@ -226,8 +226,11 @@ abstract class AbstractFilterHandler {
         $to = \CRM_Utils_Array::value("{$filterName}_to", $submittedValues);
         $fromTime = \CRM_Utils_Array::value("{$filterName}_from_time", $submittedValues);
         $toTime = \CRM_Utils_Array::value("{$filterName}_to_time", $submittedValues);
+        $op = \CRM_Utils_Array::value("op", $submittedValues);
 
-        list($from, $to) = \CRM_Utils_Date::getFromTo($relative, $from, $to, $fromTime, $toTime);
+        if ($op != 'null') {
+          list($from, $to) = \CRM_Utils_Date::getFromTo($relative, $from, $to, $fromTime, $toTime);
+        }
         if (!$from && !$to) {
           $errors[$filterName . '_relative'] = E::ts('Field %1 is required', [1 => $filterSpec->title]);
         }
@@ -477,7 +480,8 @@ abstract class AbstractFilterHandler {
       switch ($type) {
         case \CRM_Utils_Type::T_DATE:
         case \CRM_Utils_Type::T_TIMESTAMP:
-          \CRM_Core_Form_Date::buildDateRange($form, $alias, $count, '_from', '_to', E::ts('From:'), $this->isRequired(), $operations, 'searchDate', FALSE, ['class' => 'crm-select2 '.$sizeClass]);
+          $additionalOp['null'] = E::ts('Not set');
+          \CRM_Core_Form_Date::buildDateRange($form, $alias, $count, '_from', '_to', E::ts('From:'), $this->isRequired(), $additionalOp, 'searchDate', FALSE, ['class' => 'crm-select2 '.$sizeClass]);
           if (isset($defaultFilterValue['op'])) {
             $defaults[$alias . '_op'] = $defaultFilterValue['op'];
           }
@@ -584,6 +588,7 @@ abstract class AbstractFilterHandler {
     }
     switch ($type) {
       case \CRM_Utils_Type::T_DATE:
+      case \CRM_Utils_Type::T_TIMESTAMP:
         return array();
         break;
       case \CRM_Utils_Type::T_INT:
@@ -620,14 +625,23 @@ abstract class AbstractFilterHandler {
    */
   protected function applyDateFilter($submittedValues) {
     $type = $this->getFieldSpecification()->type;
+    $op = \CRM_Utils_Array::value("op", $submittedValues);
     $relative = \CRM_Utils_Array::value("relative", $submittedValues);
     $from = \CRM_Utils_Array::value("from", $submittedValues);
     $to = \CRM_Utils_Array::value("to", $submittedValues);
     $fromTime = \CRM_Utils_Array::value("from_time", $submittedValues);
     $toTime = \CRM_Utils_Array::value("to_time", $submittedValues);
 
-    list($from, $to) = \CRM_Utils_Date::getFromTo($relative, $from, $to, $fromTime, $toTime);
-
+    if ($op == 'null') {
+      $filterParams = [
+        'op' => 'IS NULL',
+        'value' => '',
+      ];
+      $this->setFilter($filterParams);
+      return TRUE;
+    } else {
+      list($from, $to) = \CRM_Utils_Date::getFromTo($relative, $from, $to, $fromTime, $toTime);
+    }
     if ($from && $to) {
       $from = ($type == "Date") ? substr($from, 0, 8) : $from;
       $to = ($type == "Date") ? substr($to, 0, 8) : $to;
diff --git a/templates/CRM/Dataprocessor/Form/Filter/DateRange.tpl b/templates/CRM/Dataprocessor/Form/Filter/DateRange.tpl
index 2126a9fb..a44bad0e 100644
--- a/templates/CRM/Dataprocessor/Form/Filter/DateRange.tpl
+++ b/templates/CRM/Dataprocessor/Form/Filter/DateRange.tpl
@@ -1,10 +1,9 @@
 {assign var=relativeName   value=$fieldName|cat:"_relative"}
-<td>
+
     {if $label}
         {ts}{$label}{/ts}<br />
     {/if}
     {$form.$relativeName.html}<br />
-</td><td>
     <span class="crm-absolute-date-range">
     <span class="crm-absolute-date-from">
       {assign var=fromName   value=$fieldName|cat:$from}
@@ -18,6 +17,7 @@
         {include file="CRM/common/jcalendar.tpl" elementName=$toName}
     </span>
   </span>
+
     {literal}
     <script type="text/javascript">
       cj("#{/literal}{$relativeName}{literal}").change(function() {
@@ -31,4 +31,3 @@
       }).change();
     </script>
     {/literal}
-</td>
diff --git a/templates/CRM/Dataprocessor/Form/Filter/GenericFilter.tpl b/templates/CRM/Dataprocessor/Form/Filter/GenericFilter.tpl
index e65bc995..a5790d57 100644
--- a/templates/CRM/Dataprocessor/Form/Filter/GenericFilter.tpl
+++ b/templates/CRM/Dataprocessor/Form/Filter/GenericFilter.tpl
@@ -4,23 +4,22 @@
 {assign var=filterMin   value=$filter.alias|cat:"_min"}
 {assign var=filterMax   value=$filter.alias|cat:"_max"}
 
-{if $filter.type == 'Date' || $filter.type == 'Timestamp'}
-    <tr>
-        <td class="label">{$filter.title}</td>
-        {include file="CRM/Dataprocessor/Form/Filter/DateRange.tpl" fieldName=$filter.alias from='_from' to='_to'}
-    </tr>
-{elseif $form.$fieldOp.html}
-    <tr>
-        <td class="label">{$filter.title}</td>
-        <td>
-          <span class="filter-processor-element {$filter.alias}">{$form.$fieldOp.html}</span>
-          <span class="filter-processor-show-close {$filter.alias}">&nbsp;</span>
-        </td>
-        <td>
-            <span id="{$filterVal}_cell">{$form.$filterVal.label}&nbsp;{$form.$filterVal.html}</span>
-            <span id="{$filterMin}_max_cell">{$form.$filterMin.label}&nbsp;{$form.$filterMin.html}&nbsp;&nbsp;{$form.$filterMax.label}&nbsp;{$form.$filterMax.html}</span>
-        </td>
-    </tr>
-{/if}
+<tr>
+    <td class="label">{$filter.title}</td>
+    <td>
+      {if $form.$fieldOp.html}
+      <span class="filter-processor-element {$filter.alias}">{$form.$fieldOp.html}</span>
+      <span class="filter-processor-show-close {$filter.alias}">&nbsp;</span>
+      {/if}
+    </td>
+    <td>
+      {if $filter.type == 'Date' || $filter.type == 'Timestamp'}
+          {include file="CRM/Dataprocessor/Form/Filter/DateRange.tpl" fieldName=$filter.alias from='_from' to='_to'}
+      {else}
+          <span id="{$filterVal}_cell">{$form.$filterVal.label}&nbsp;{$form.$filterVal.html}</span>
+          <span id="{$filterMin}_max_cell">{$form.$filterMin.label}&nbsp;{$form.$filterMin.html}&nbsp;&nbsp;{$form.$filterMax.label}&nbsp;{$form.$filterMax.html}</span>
+      {/if}
+    </td>
+</tr>
 
 {/crmScope}
-- 
GitLab